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~
附上我认为比较值得研读的相关文章: