React中使用useState()导致的问题记录


https://www.jianshu.com/p/d9158074176b

场景一: 更新 state 的一个对象(或数组)属性的某个子属性或值。

使用 Hook Function Component

function App() { const [arr, updateArr] = useState([]); const addList = () => { arr.push('Hello React'); updateArr(arr); }; return ( <div> { arr.map((item, index) => ( <p key={index}>{index} {item}</p> )) } <button onClick={addList}>添加List</button> </div> ); } 

使用 Class Component

class App extends Component { constructor(props) { super(props); this.state = { arr: [] } } addList = () => { let arr = this.state.arr; arr.push('Hello React'); this.setState({ arr: arr }, () => { console.log(this.state.arr); }); }; deleteList = () => { const { arr } = this.state; arr.splice(0, 1); this.setState({ arr: arr }, () => { console.log(this.state.arr); }); }; render() { const { arr } = this.state; return ( <div> { arr.map((item, index) => ( <p key={index}>{index} {item}</p> )) } <button onClick={this.addList}>添加List</button> <button onClick={this.deleteList}>删除List</button> </div> ); } } 

结果:使用 Hook Function Component push 数组后数组长度并没有改变,使用Class Component正常。
原因:在 Hook 中直接修改 state 的一个对象(或数组)属性的某个子属性或值,然后直接进行 set,不会触发重新渲染。

  • 对 Class Component来说,state 是 Immutable 的,setState 后一定会生成一个全新的 state 引用。它是通过 this.state 方式读取 state,所以每次代码执行都会拿到最新的 state 引用。
  • 对 Hook Function Component 而言,useState 产生的数据也是 Immutable 的,通过数组第二个参数 Set 一个新值后,原来的值会形成一个新的引用在下次渲染时。

解决方案

改变引用地址

function App() { const [arr, updateArr] = useState([]); const addList = () => { arr.push('Hello React'); // 通过扩展运算符实现深拷贝 updateArr([...arr]); }; return ( <div> { arr.map((item, index) => ( <p key={index}>{index} {item}</p> )) } <button onClick={addList}>添加List</button> </div> ); } 

场景二: 在setTimeout中更改state。

使用 Hook Function Component

function App() { const [count, updateCount] = useState(0); useEffect(() => { let timer = setTimeout(() => { updateCount(1); getCount(); }, 1000); return () => { clearTimeout(timer); } }, []); const getCount = () => { console.log(count); // result: 0 }; return ( <div>{count}</div> ); } 

使用 Class Component

let timer = null; export default class App extends Component { constructor(props) { super(props); this.state = { count: 0 } } componentDidMount() { timer = setTimeout(() => { this.setState({ count: 1 }) this.getCount(); }, 1000); } getCount = () => { console.log(this.state.count); // result: 1 } componentWillUnmount() { clearTimeout(timer); } render() { const { count } = this.state; console.log(count); // result: 1 return ( <div>{count}</div> ); } } 

结果:使用 Hook Function Component 更改count后,页面显示1,getCount方法中打印的count为0,使用Class Component更改count后页面显示1,getCount方法中打印的count为1。
原因:Hook Function Comoponent中由于对 state 的读取没有通过 this. 的方式,使得每次 setTimeout 都读取了当时渲染闭包环境的数据,虽然最新的值跟着最新的渲染变了,但旧的渲染里,状态依然是旧值。

解决方案

使用ref

function App() { const [count, updateCount] = useState(0); useEffect(() => { let timer = setTimeout(() => { updateCount(1); getCount(); }, 1000); return () => { clearTimeout(timer); } }, []); let ref = useRef(); ref.current = count; const getCount = () => { console.log(ref.current); // result: 1 }; return ( <div>{count}</div> ); } 

The End~

附上我认为比较值得研读的相关文章:

1.react-hook-usestate-setState
2.精读《Function Component 入门》


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM