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~
附上我認為比較值得研讀的相關文章: