setState 是如何给 state 赋值的
- 通过
Object.assign()
import React from 'react'; class Home extends React.Component { constructor(props) { super(props); this.state = { name: 'yangbuyiya', age: 18 } let oldObj = {name: 'yangbuyiya', age: 18}; let newObj = {age: 666}; let obj = Object.assign({}, oldObj, newObj); console.log(obj); } render() { return ( <div> <p>{this.state.name}</p> <p>{this.state.age}</p> <button onClick={() => { this.btnClick() }}>按钮 </button> </div> ) } btnClick() { this.setState({ age: 666 }); } } class App extends React.Component { render() { return ( <div> <Home/> </div> ) } } export default App;
state 合并现象
- 因为
setState
会收集一段时间内所有的修改操作,然后在统一的执行,再更新界面 - 所以就出现了 state 的合并现象
首先来看一个案例,然后引出这个 state 的合并场景先如下:
import React from 'react'; class Home extends React.Component { constructor(props) { super(props); this.state = { age: 0 } } render() { return ( <div> <p>{this.state.age}</p> <button onClick={() => { this.btnClick() }}>按钮 </button> </div> ) } btnClick() { this.setState({ age: this.state.age + 1 }); this.setState({ age: this.state.age + 1 }); this.setState({ age: this.state.age + 1 }); console.log(this.state.age); } } class App extends React.Component { render() { return ( <div> <Home/> </div> ) } } export default App;
然后查看结果发现居然是 1:
为什么最终的一个值是1, 不是 3 呢是吧,我明明是进行增加了 3 次加 1 的操作,因为 setState 默认是一个异步的方法, 默认会收集一段时间内所有的更新, 然后再统一更新, 所以就导致了最终的一个值是 1, 不是 3,博主可以大致的提供一下它底层的实现代码这样可以更加的让你对 setState 有更深层次的理解,如下:
let oldObj = {age: 0}; let stateList = [ // 演变过程1 // {age: oldObj.age + 1}, // {age: oldObj.age + 1}, // {age: oldObj.age + 1}, // 演变过程2 // {age: 0 + 1}, // {age: 0 + 1}, // {age: 0 + 1}, // 演变过程3 {age: 1}, {age: 1}, {age: 1} ]; stateList.forEach((newObj) => { // 演变过程1 // Object.assign({}, {age: 0}, {age: 1}); // {age: 1} // 演变过程2 // Object.assign({}, {age: 1}, {age: 1}); // {age: 1} // 演变过程3 // Object.assign({}, {age: 1}, {age: 1}); // {age: 1} oldObj = Object.assign({}, oldObj, newObj); }); console.log(oldObj);
解决 state 合并现象
第一种方案就是前面所说的通过 setState 方法的第二个参数, 通过回调函数拿到更新之后的值,然后在根据该值在进行加一操作如下:
import React from 'react'; class Home extends React.Component { constructor(props) { super(props); this.state = { age: 0 } } render() { return ( <div> <p>{this.state.age}</p> <button onClick={() => { this.btnClick() }}>按钮 </button> </div> ) } btnClick() { this.setState({ age: this.state.age + 1 }, () => { this.setState({ age: this.state.age + 1 }, () => { this.setState({ age: this.state.age + 1 }); }); }); console.log(this.state.age); } } class App extends React.Component { render() { return ( <div> <Home/> </div> ) } } export default App;
但是上面的代码存在弊端,层级结构比较深,以后维护比较不友好,所以说 React 也考虑到了这一点,所以这里就引出了第二种解决方案,通过 setState 的第一个参数来进行解决,第一个参数除了可以传递一个对象,其实还可以传递一个回调函数,回调函数有两个默认的参数第一个就是上一次更新的最新的值,然后我们可以在该回调函数中就可以直接拿到最新的值,就不会出现合并的现象了。
import React from 'react'; class Home extends React.Component { constructor(props) { super(props); this.state = { age: 0 } } render() { return ( <div> <p>{this.state.age}</p> <button onClick={() => { this.btnClick() }}>按钮 </button> </div> ) } btnClick() { this.setState((preState, props) => { return {age: preState.age + 1}; }); this.setState((preState, props) => { return {age: preState.age + 1}; }); this.setState((preState, props) => { return {age: preState.age + 1}; }); console.log(this.state.age); } } class App extends React.Component { render() { return ( <div> <Home/> </div> ) } } export default App;
那么为什么这样就可以解决从 0 变为 3 呢,这里也提供出它底层的实现,和演变过程,可以更深层次的加深理解:
let oldObj = {age: 0}; let stateList = [ (preState) => { return {age: preState.age + 1} }, (preState) => { return {age: preState.age + 1} }, (preState) => { return {age: preState.age + 1} }, ]; stateList.forEach((fn) => { // 演变过程1 // {age: 1} // 演变过程2 // {agg: 2} // 演变过程3 // {agg: 3} let newObj = fn(oldObj); // 演变过程1 // {age: 0} {age: 1} / {age: 1} // 演变过程2 // {age: 1} {age: 2} / {age: 2} // 演变过程3 // {age: 2} {age: 3} / {age: 3} oldObj = Object.assign({}, oldObj, newObj); }); console.log(oldObj);
最后
本期结束咱们下次再见👋~
🌊 关注我不迷路,如果本篇文章对你有所帮助,或者你有什么疑问,欢迎在评论区留言,我一般看到都会回复的。大家点赞支持一下哟~ 💗