setState和useState的状态变更有什么区别嘛?

Rnorth
  • 498

rt:这两者在选择数据合并处理上会有什么异同嘛?
比如说我多次调用setState和useState数组结构出的dispatcher处理状态变更
变更合并等处理上会有什么区别吗?

以下代码会输出:0000

const Example = () => {
  const [state, stateDispatcher] = useState(0);
  useEffect(() => {
    stateDispatcher(state + 1);
    console.log(state);
    stateDispatcher(state + 1);
    console.log(state);

    setTimeout(() => {
      stateDispatcher(state + 1);
      console.log(state);
      stateDispatcher(state + 1);
      console.log(state);
    }, 100);
  }, []);
  return null;
};
export default Example;

以下为:0011...

const Example = () => {
  const [state, stateDispatcher] = useState(0);
  stateDispatcher(state + 1);
  console.log(state);
  stateDispatcher(state + 1);
  console.log(state);

  setTimeout(() => {
    stateDispatcher(state + 1);
    console.log(state);
    stateDispatcher(state + 1);
    console.log(state);
  }, 100);
  return null;
};
export default Example;

以下代码输出0023

class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      val: 0,
    };
  }

  componentDidMount() {
    this.setState({ val: this.state.val + 1 });
    console.log(this.state.val); 

    this.setState({ val: this.state.val + 1 });
    console.log(this.state.val); 

    setTimeout(() => {
      this.setState({ val: this.state.val + 1 });
      console.log(this.state.val); 

      this.setState({ val: this.state.val + 1 });
      console.log(this.state.val); 
    }, 100);
  }

  render() {
    return null;
  }
}

export default Example;
回复
阅读 1.8k
3 个回答
  1. useStatesetState的状态变更没区别
  2. 上面三个demo为啥输出不同?
    纯属写法不同导致的。

首先要先理解几个概念:

  • 更新队列
  • 批处理
  • 立即计算
  • 懒计算
  • 跳过更新

如果需要科普可以看看这个文章解密React state hook

  1. 解析问题中的3个demo:
    好像已经不需要解释了。
  • demo2是个死循环,稍稍修改下代码:

    const Example = () => {
    const [state, stateDispatcher] = useState(0);
    if (state === 0) {
      stateDispatcher(state + 1); // 会跳过`state=0`的渲染
      console.log(state);
      stateDispatcher(state + 1); 
      console.log(state);
    }
    
    useEffect(() => {
      console.log(`useEffect`); // 观察这个输出次数
    
      setTimeout(() => {
        stateDispatcher(state + 1); 
        console.log(state);
        stateDispatcher(state + 1);
        console.log(state);
      }, 100);
    }, []);
    
    return null;
    };
    export default Example;
  • 修改demo3写法,使其和demo1输出一致:

    class Example extends React.Component {
    constructor() {
      super();
      this.state = {
        val: 0
      };
    }
    
    componentDidMount() {
      // 调整demo3的写法
      const { val } = this.state;
      this.setState({ val: val + 1 });
      console.log(val);
    
      this.setState({ val: val + 1 });
      console.log(val);
    
      setTimeout(() => {
        this.setState({ val: val + 1 });
        console.log(val);
    
        this.setState({ val: val + 1 });
        console.log(val);
      }, 100);
    }
    
    render() {
      return null;
    }
    }
    
    export default Example;

说下我的理解
本身setState和useState的状态更新应该没什么区别,都是加入到更新队列中,下个周期合并执行,而在settiomeout当中不合并执行;例2死循环不讨论,例3应该比较好理解,setState更新不会立刻生效,settiomeout不合并更新,例1表现的跟例3不一样的原因在于useEffect callback形成了一个大的闭包,跟外包state进行了捆绑,而又由于useEffect为设置依赖,所以导致内部state一直关联的是初始state并不会更新

核心机制上没有区别,用法上是有差异的。主要是还是你的测试代码编写思路的问题,useEffect(fn, [])componentDidMount只是表示组件已经被加载了,实时上与你的测试目的关系并不大。
首先明确,对state的修改是异步的,所以事实上合并在何时发生是无法预测的,就像是同时发送两个请求,一定要用Promise.all()才能保证两个请求都完成了,而不是第二个完成了第一个必然完成。
因此,建议还是用const [a, setA] = useState(0);搭配useEffect(fn, [a])setState搭配componentDidUpdate进行测试。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏