[ReactDoc] React Hooks - API Reference
Hooks API Reference @ React Docs
useState
useState @ React Docs - Hooks API Reference
⚠️ 特別留意:在 useState
的 set function 和 Class 中的 setState
不同,useState 的 set function 不會主動 merge,因此可以透過 { ...preObject }
的用法複製完整的物件。
functional updater:使用前一次的資料狀態
如果有需要的話,在 setCount
的函式中可以帶入 function,這個 function 可以取得前一次的資料狀態:
const [foo, setFoo] = useState(initialFoo);
setFoo((prevState) => /* do something with prevState */)
使用範例:
function Counter({ initialCount }) {
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount((prevCount) => prevCount + 1)}>+</button>
<button onClick={() => setCount((prevCount) => prevCount - 1)}>-</button>
</>
);
}
⚠️ 在 useEffect 或 useCallback 中善用 prevState 避免狀態依賴
若我們在 setCount
中使用的是前一次的狀態,就可以把該狀態從相依陣列(dependency array)中移除,這麼做的好處是畫面會隨時間繼續重新 render,但 useEffect 和裡面的 cleanup function 並不會每次都被叫到,像是這樣:
其他範例 如何錯誤地使用 React hooks useCallback 來保存相同的 function instance:
import React, { useState, useCallback, useRef } from 'react';
import ReactDOM from 'react-dom';
import './styles.css';
const Button = React.memo(({ handleClick }) => {
const refCount = useRef(0);
return <button onClick={handleClick}>{`button render count ${refCount.current++}`}</button>;
});
function App() {
const [isOn, setIsOn] = useState(false);
// 在 setIsOn 中帶入 prevState 可以避免把 state 或 props 放入相依陣列中
const handleClick = useCallback(() => setIsOn((isOn) => !isOn), []);
return (
<div className="App">
<h1>{isOn ? 'On' : 'Off'}</h1>
<Button handleClick={handleClick} />
</div>
);
}
const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);
root.render(<App />);
Lazy initial state
- Lazy initial state @ React Docs - API -Reference
- How to create expensive objects lazily? @ Hooks FAQ
如果在 useState
內的預設值有些是需要經過函式處理的,則在 useState
的參數中可以帶入函式:
// 沒使用 lazy initial state: someExpensiveComputation 每次 render 都會被呼叫
const [state, setState] = useState(someExpensiveComputation(props));
// 使用 lazy initial state: someExpensiveComputation 只會被呼叫一次
const [state, setState] = useState(() => someExpensiveComputation(props));
useEffect vs. useLayoutEffect
簡單來說 useEffect
會阻塞畫面的轉譯(類似 addEventListener 中把 passive
設成 false
),需要等到 useEffect 內的 JS 執行完成後;而 useLayoutEffect
則不會阻塞畫面的轉譯(類似把 passive
設成 true
),畫面會持續轉譯,而不會被 useLayoutEffect
內的 JS 執行給阻塞。
useLayoutEffect @ React Docs
useRef
useRef @ React Docs - Hooks API Reference
由於每次畫面轉譯時的 state, props 都是獨立的,因此若我們真的有需要保留某一次轉譯時的資料狀態,則可以使用 useRef
。
const refContainer = useRef(<initialValue>);
useRef
會回傳有一個帶有 .current
屬性的物件,這個 .current
是可以被改變的(mutable),同時會在該元件的完整生命週期中被保留。透過 useRef
的參數,可以把初始值帶入 .current
屬性中。