React:关于React中setstate的同步或异步。
首先,我们需要理解同步和异步的含义。让我们往下看。
在 React 在类型组件中,我们可以使用setstate方法更新state状态。但有时在使用setstate后,我们无法获得最新的数据。
其实 React 中setstate本身执行的过程和代码是同步的,只是因为 React 由于框架本身的性能优化机制。
React 更新前,合成事件和生命周期函数的调用顺序导致合成事件和生命周期函数中无法立即得到更新的值,形成异步形式。
如果Setstate方法在合成事件中被循环调用n次,如果 React 没有优化,当前组件将被渲染n次,这是对性能的巨大浪费。因此,React 由于性能原因,将多个setstate方法合并为一个执行。执行setstate时,state中的数据不会立即更新。
前面已经说过了,在 React setstate直接调用于合成事件和生命周期函数,会表现出异步的形式。
另外,如果越过 React 在原生事件、settimeout中使用setstate的性能优化机制,将表现出同步的形式。
表现为异步
React 合成事件
在 React 直接使用的事件,如onchange等、onclick等,都是由 React 包装后的事件由合成事件组成 React 管理。因此,由于性能优化的机制,setstate直接调用于合成事件,将表现出异步的形式。
在合成事件onclick中,直接将state中的count添加到以下代码中,然后打印count值。因此,当您第一次点击按钮时,您将打印0,而不是最新的1。
state = { count: 0 }; add = () => { this.setState({ count: this.state.count 1 }); console.log(this.state.count); // 0 }; render() { return ( <> <div>当前计数:{this.state.count}</div> <button onClick={this.add}>add</button> </> ); }
生命周期函数
生命周期函数也是由的 React 在生命周期函数中直接调用setstate的管理也会表现出异步的形式。
在生命周期componentdidmount函数中,将state中的count加1,然后打印count值,打印0,而不是最新的1。
state = { count: 0 }; componentDidMount() { this.setState({ count: this.state.count 1 }); console.log(this.state.count); // 0 } render() { return ( <> <div>当前计数:{this.state.count}</div> <button>add</button> </> ); }
表现为同步
原生事件
setstate本身的执行过程是同步的,使用原始事件绕过 React 管理,将表现出同步的形式。
通过id获取以下代码 DOM 元素,用原始方法绑定点击事件。在点击事件中,将state中的count添加到1,然后打印count值,最新的count值将打印到1。
state = { count: 0 }; componentDidMount() { const btn = document.getElementById('btn'); btn.onclick = () => { this.setState({ count: this.state.count 1 }); console.log(this.state.count); // 1 }; } render() { return ( <> <div>当前计数:{this.state.count}</div> <button id="btn">add</button> </> ); }
setTimeout
以下代码在生命周期compontdidmount函数中写下定时器settimeout,在settimeout中添加state中的count1,然后打印count值,最新count值1将打印。
虽然setstate也写在生命周期componentdidmount函数中,但它不是直接写在componentdidmount中,而是设置了一层setttimeout。这样,setstate就显示出同步的形式。
state = { count: 0 }; componentDidMount() { setTimeout(() => { this.setState({ count: this.state.count 1 }); console.log(this.state.count); // 1 }, 0); } render() { return ( <> <div>当前计数:{this.state.count}</div> <button>add</button> </> ); }
setstate的第二个参数
setstate的对象写法和函数写法都有第二个参数,这是可选的回调函数。这个回调函数只有在状态更新和界面更新后才能调用(render调用后)。
如下代码所示,虽然setstate直接在componentdidmount中调用,但在setstate的回调函数中打印count的值得到最新值1,因为回调函数在状态更新后才被调用,当然可以得到最新的count。
state = { count: 0 }; componentDidMount() { this.setState({ count: this.state.count 1 }, () => { console.log(this.state.count); // 1 }); } render() { return ( <> <div>当前计数:{this.state.count}</div> <button>add</button> </> ); }