what is hook?
- hook就是JavaScript函数,这个函数可以帮你勾入React State以及生命周期等特性;useState=>钩入状态 useEffect=>钩入生命周期;
- 只能在函数顶层去调用hook,不能在if语句和for循环里面去调用hook;
- 普通函数里面不能使用hooks;
- 自定义hooks,use开头,可以使用react提供的其他hooks,必须是use开头。
why use hooks?
- Keep state logic close to where it is used: 不需要像 Class 再分 View 跟 Lifecycle 的檔案;
- Often requires less code and less prop passing: 可以用更少的程式碼撰寫;
- Can be more granular: 只需要更新 specific dependencies 變動就好;
- Improves reliability and allows for logical grouping of functionality;
- 藉由 custom hook 更容易 reuse
useState
會回傳一個包含兩個值的 array,第一個值是 state、第二個值是用來更新 state 的函式。每當 state 值改變,就會觸發 re-render
const [appleCount, setAppleCount] = useState(1); console.log(appleCount); //1 setAppleCount(prev => prev + 1); console.log(appleCount) // 2
以上程式碼就代表 appleCount
這個變數的初始值是 1
,只有用 setAppleCount
這個更新函式更新 appleCount
才會觸發 re-render
要用 setCount(count + 1) 還是 setCount(prev => prev + 1) ?
更新 state 的函式可以丟兩種參數
- Pass the state, Run Everytime. eg.
setCount(count + 1)
- Pass the function, Run only the very first time when your component render. eg.
setCount(prev => prev + 1)
兩種都可以用,但需要了解其中有什麼不同,若懶得了解那建議用後者可以減少出錯機會。以下先出一道題
1 function A () { 2 const [count, setCount] = useState(4); 3 setCount(count + 1); 4 setCount(count + 1); 5 console.log('A: ', count) // ? 6 } 7 function B () { 8 const [count, setCount] = useState(4); 9 setCount(prev => prev + 1); 10 setCount(prev => prev + 1); 11 console.log('B: ', count) // ? 12 } 13 // Answer 14 // A: 5 15 // B 6
有沒有答對呢? 在 A 裡面第二個 setCount
會覆蓋第一個,因為他們抓的 count 都是 4,但若是用 function version 來設定 state 就會記住 prevState 再去做運算
另外 Pass the state 是每一次都會重新跑,而 Pass the function 只會跑第一次,直接來看 範例 比較快 https://codepen.io/hannahpun/pen/VwjxPWM
這就是狀態的延遲初始化,每当 React 重新渲染组件时,都会执行useState(initialState)
。 如果初始状态是原始值(数字,布尔值等),则不会有性能问题。
当初始状态需要昂贵的性能方面的操作时,可以通过为useState(computeInitialState)
提供一个函数来使用状态的延迟初始化
getInitialState()
仅在初始渲染时执行一次,以获得初始状态。在以后的组件渲染中,不会再调用getInitialState()
,从而跳过昂贵的操作。
1 function init () { 2 console.log('run function'); 3 return 4; 4 } 5 // Run Everytime 6 const [count, setCount] = useState(4); 7 const [count, setCount] = useState(init()); 8 // Run only the very first time when your component render 9 const [count, setCount] = useState(() => init());
若用object 數據的不可變性
1 const [state, setState] = useState({count: 4, name: 'blue'}); 2 setState(prevState => {...prevSate, count: prevSate.count + 1}; 3 console.log(state); // {count: 5, name: 'blue'}setState(prevState => {count: prevSate.count + 1}; 4 console.log(state); // {count: 5} name 消失,因為他會整個覆蓋掉。
过时状态
闭包是一个从外部作用域捕获变量的函数。
闭包(例如事件处理程序,回调)可能会从函数组件作用域中捕获状态变量。 由于状态变量在渲染之间变化,因此闭包应捕获具有最新状态值的变量。否则,如果闭包捕获了过时的状态值,则可能会遇到过时的状态问题。
来看看一个过时的状态是如何表现出来的。组件<DelayedCount>
延迟3
秒计数按钮点击的次数。
1 function DelayedCount() { 2 const [count, setCount] = useState(0); 3 4 const handleClickAsync = () => { 5 setTimeout(function delay() { 6 setCount(count + 1); 7 }, 3000); 8 } 9 10 return ( 11 <div> 12 {count} 13 <button onClick={handleClickAsync}>Increase async</button> 14 </div> 15 ); 16 }
count
变量不能正确记录实际点击次数,有些点击被吃掉。
delay()
是一个过时的闭包,它从初始渲染(使用0
初始化时)中捕获了过时的count
变量。
为了解决这个问题,使用函数方法来更新count
状态:
1 function DelayedCount() { 2 const [count, setCount] = useState(0); 3 4 const handleClickAsync = () => { 5 setTimeout(function delay() { 6 setCount(count => count + 1); 7 }, 3000); 8 } 9 10 return ( 11 <div> 12 {count} 13 <button onClick={handleClickAsync}>Increase async</button> 14 </div> 15 ); 16 }
标签:count,function,const,setCount,state,useState From: https://www.cnblogs.com/sheldon-SJ/p/17493497.html