React 官方教學區 https://react.dev/learn/describing-the-ui
State
State 的用途:
- A state variable to retain the data between renders.
- A state setter function to update the variable and trigger React to render the component again.
什麼是 Rendering?
- “Rendering” means that React is calling your component, which is a function. The JSX you return from that function is like a snapshot of the UI in time. Its props, event handlers, and local variables were all calculated using its state at the time of the render.
- render 階段時,state 的值就已經決定了。在執行時要能拿到最新的 state 值需要在 set 裡面使用 update function。
每次的 Render 都有自己的輸入,我們希望輸入來自不變的值,或 function 內部的資料(state&ref),而不是依賴外部的值(=產生 side effect)
Pure Function & Side Effect
Pure Function:
- 同樣的 input 有同樣的 output
- 只更改 function 內部,不去影響外部
- 我們希望我們的元件都是 Pure Function,不要發生非預期的事情
- 為什麼要 pure?
- 結果好預期
- 執行中斷可重來
- 所有環境下都是同樣結果
- 從輸入不變結果不變這點可以做效能的優化
- Strict Mode 可以幫助檢查元件是不是 Pure Function
Rendering:
- 執行 function,回傳 JSX。不包含改變畫面。
Side Effect:
- A React side-effect occurs when we use something that is outside the scope of React. eg. local starage, function 外的變數
Good Side Effect:
- 在 render process 後發生 => 更新畫面, 改變狀態。
Bad Side Effect:
- React’s rendering process must always be pure. Components should only return their JSX, and not change any objects or variables that existed before rendering—that would make them impure!
Bad Case
let guest = 0;
function Cup() {
// Bad: changing a preexisting variable!
guest = guest + 1;
return <h2>Tea cup for guest #{guest}</h2>;
}
export default function TeaSet() {
return (
<>
<Cup />
<Cup />
<Cup />
</>
);
Rerender
- 更新畫面
- Any screen update in a React app happens in three steps:
- Trigger
- Render
- Commit
- You can use Strict Mode to find mistakes in your components
- React does not touch the DOM if the rendering result is the same as last time
useRef
用法1
- React 每次 render 都有自己的資料。當需要在每次 render 間記得一個變數時可以用 state 或 ref 方法。
- 使用 ref 的時機:只需要更新資料時但不需要 rerender。
- 常見的錯誤警告:在 rendering 階段讀寫 ref 值。
用法2
- 使用
ref
保存 DOM Node - 技巧:ref + callback function 保存多個節點
- ref 裡面的資料改用 map 或 物件資料結構
- DOM Nodes 是在什麼時間點被保存當 ref 中?
- 會先設 ref 為 null,再去抓 DOM
forwardRef
- 動機:想取得另一個元件的 DOM Node Ref
- 在父層使用 forwardRef 可以將 ref 暴露給子層元件
useImperativeHandle
- 動機:直接 return 這個 ref 有點危險,使用的人可以對這個 ref 做所有事。如果想封裝這個 ref,讓使用的人只能使用我有開放的 api。
- 在 forwardRef 內使用 useImperativeHandle,裡面定義開放的屬性或方法。
Effect
Synchronizing with Effects
- = synchronize your component with some system outside of React.
今晚我想來點 effect
effect 可以放在哪?
- render 後都可,eg. event handler
- 想在 render 後自動執行,eg. 載入元件後自動拉資料 => useEffect
基本 useEffect 用法
- dependency
- cleanup function
什麼東西要綁 dependency?
- eslint 教你
什麼東西要 cleanup?
- componet 消失
- 註冊了事件 eg. timeout
- cancel all subscriptions made and cancel fetch requests
沒 cleanup 會怎樣?
- 有些執行兩次沒關係,但有些會造成錯誤
- dialog 打開前應該要是關閉狀態
- 註冊事件重複註冊會 memory leak
- fetch 兩次資料,第一次應該要取消避免使用者不小心看到舊資料
- 使用好輪子: uesQuery, SWR 可以幫你處理 race condition, 重複請求
檢查 cleanup
- Strict Mode
- dev env 會幫忙檢查 cleanup 有沒有漏寫。eg. 執行了兩次 effect
you-might-not-need-an-effect
Case state 依賴外部 props
// Better: Adjust the state while rendering
const [prevItems, setPrevItems] = useState(items);
if (items !== prevItems) {
setPrevItems(items);
setSelection(null);
}
// ...
如果 state 依賴外部 props 的做法,可以多用一個 if 判斷來做同步,不需要用到 effect。壹定要有 if,不然會變成 loop。
Case 想要初始值
key={userId}
使用 key。當 key 改變 component 會回到預設值,不需要用寫 effect 依賴 userId。
Ref
- 2022 ithome 我讀你看系列