setState传递函数和传递值的区别

156 阅读2分钟

1 传递值

  • 下面这段代码会打印什么?屏幕的count是1还是2?
import React, { useState } from "react";
import ReactDOM from "react-dom";
function App() {
  const [count, setCount] = useState(0);
  console.log("render", count);
  const handleBtnClick = () => {
    setCount(count + 1);
    setCount(count + 1);
  };
  return (
    <div className="App">
      <button onClick={handleBtnClick}>++</button>
      <span>count:{count}</span>
    </div>
  );
}
ReactDOM.render(<App />, document.getElementById("root"));

image.png

2 传递函数

  const handleBtnClick = () => {
    //setCount(count + 1);
    //setCount(count + 1);
    setCount((count) => count + 1);
    setCount((count) => count + 1);
  };
  • 可以看到页面渲染的count是2

image.png

let newState = current.baseState;

  • 上面两次setCount创建了两个update,传入的函数或者值分别保存在了对应update的action属性上,在重新render阶段时运行函数,调用useState计算新的state;会遍历此hook上的hook.queue链表上保存的update,调用reducer方法计算newState;
let newState = current.baseState;//取上一次更新的state
do {
    ...省略细节
    const action = update.action;
    newState = reducer(newState, action);
    update = update.next;
} while (update !== null && update !== first);
//计算完成后更新memoizedState并返回
hook.memoizedState = newState;
hook.baseState = newBaseState;
return [hook.memoizedState, dispatch];
  • 这里关键是这个reducer的计算逻辑,实质上useState是预置了basicStateReducer的useReducer
function basicStateReducer(state, action) {
  return typeof action === 'function' ? action(state) : action;
}
  • 从basicStateReducer中可以看出传函数和传值的区别,当传入的action是函数时,每次调用reducer(newState, action)都会传入上一次计算得到的新的newState进去调用action(state)返回新的结果;而如果传值的话则newState的结果只会是最后一个update对象的action的值,这里两次setCount(count + 1)传入的都是count+1表达式的结果1(因为js的函数参数是传值不是传引用,这里调用setCount前会先计算表达式count + 1的结果0+1等于1),又因为react的batchedUpdates合并更新所以两次setCount(count + 1)中读取到的count都是本次render的值0;

  • 下面我们再修改下代码验证一下,调用三次setCount(),但在最后一次传入的是值的情况

function App() {
  const [count, setCount] = useState(0);
  console.log("render", count);
  const handleBtnClick = () => {
    setCount((count) => count + 1);
    setCount((count) => count + 1);
    setCount(count + 1);
  };
  return (
    <div className="App">
      <button onClick={handleBtnClick}>++</button>
      <span>count:{count}</span>
    </div>
  );
}
ReactDOM.render(<App />, document.getElementById("root"));
  • 发现最终渲染的count是1,符合预期 image.png

3 对于class组件,情况是一致的

  • 传递值
class App extends React.Component {
  state = {
    count: 0,
  };
  handleBtnClick = () => {
    const { count } = this.state;
    this.setState({
      count: count + 1,
    });
    this.setState({
      count: count + 1,
    });
  };
  render() {
    const { count } = this.state;
    console.log("render", count);
    return (
      <div className="App">
        <button onClick={this.handleBtnClick}>++</button>
        <span>count:{count}</span>
      </div>
    );
  }
}

image.png

  • 传递函数
class App extends React.Component {
  state = {
    count: 0,
  };
  handleBtnClick = () => {
    this.setState((state) => ({ count: state.count + 1 }));
    this.setState((state) => ({ count: state.count + 1 }));
  };
  render() {
    const { count } = this.state;
    console.log("render", count);
    return (
      <div className="App">
        <button onClick={this.handleBtnClick}>++</button>
        <span>count:{count}</span>
      </div>
    );
  }
}

image.png