[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fstate":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fstate":511,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fstate":535},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":15,"pinned":16,"draft":16,"body":17,"seo":507,"stem":508,"id":509,"extension":510},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fstate","State 与 Reducer","Hooks 让函数组件拥有 state 与副作用能力。本文介绍 useState 与 useReducer。见 React 基础。","技能小册",[12,13,14],"react","hooks","state","2021-02-21",false,{"type":18,"value":19,"toc":500},"minimark",[20,24,43,47,124,200,203,210,244,247,250,399,411,415,433,442,445,448,496],[21,22,8],"h1",{"id":23},"state-与-reducer",[25,26,27,28,32,33,36,37,42],"p",{},"Hooks 让函数组件拥有 state 与副作用能力。本文介绍 ",[29,30,31],"code",{},"useState"," 与 ",[29,34,35],{},"useReducer","。见 ",[38,39,41],"a",{"href":40},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fbasics","React 基础","。",[44,45,31],"h2",{"id":46},"usestate",[48,49,54],"pre",{"className":50,"code":51,"language":52,"meta":53,"style":53},"language-tsx shiki shiki-themes material-theme-lighter github-light github-dark","import { useState } from \"react\";\n\nfunction Counter() {\n  const [count, setCount] = useState(0);\n\n  return (\n    \u003Cbutton type=\"button\" onClick={() => setCount((c) => c + 1)}>\n      {count}\n    \u003C\u002Fbutton>\n  );\n}\n","tsx","",[29,55,56,64,71,77,83,88,94,100,106,112,118],{"__ignoreMap":53},[57,58,61],"span",{"class":59,"line":60},"line",1,[57,62,63],{},"import { useState } from \"react\";\n",[57,65,67],{"class":59,"line":66},2,[57,68,70],{"emptyLinePlaceholder":69},true,"\n",[57,72,74],{"class":59,"line":73},3,[57,75,76],{},"function Counter() {\n",[57,78,80],{"class":59,"line":79},4,[57,81,82],{},"  const [count, setCount] = useState(0);\n",[57,84,86],{"class":59,"line":85},5,[57,87,70],{"emptyLinePlaceholder":69},[57,89,91],{"class":59,"line":90},6,[57,92,93],{},"  return (\n",[57,95,97],{"class":59,"line":96},7,[57,98,99],{},"    \u003Cbutton type=\"button\" onClick={() => setCount((c) => c + 1)}>\n",[57,101,103],{"class":59,"line":102},8,[57,104,105],{},"      {count}\n",[57,107,109],{"class":59,"line":108},9,[57,110,111],{},"    \u003C\u002Fbutton>\n",[57,113,115],{"class":59,"line":114},10,[57,116,117],{},"  );\n",[57,119,121],{"class":59,"line":120},11,[57,122,123],{},"}\n",[125,126,127,140],"table",{},[128,129,130],"thead",{},[131,132,133,137],"tr",{},[134,135,136],"th",{},"要点",[134,138,139],{},"说明",[141,142,143,159,170,189],"tbody",{},[131,144,145,153],{},[146,147,148,149],"td",{},"更新是",[150,151,152],"strong",{},"替换",[146,154,155,158],{},[29,156,157],{},"setCount(1)"," 用新值替换旧 state",[131,160,161,164],{},[146,162,163],{},"函数式更新",[146,165,166,169],{},[29,167,168],{},"setCount(c => c + 1)"," 基于上一状态计算，适合连续更新",[131,171,172,175],{},[146,173,174],{},"批量更新",[146,176,177,178,181,182,188],{},"同一事件处理函数内多次 ",[29,179,180],{},"setState","，React 18 会批量处理后再渲染（见 ",[38,183,187],{"href":184,"rel":185},"https:\u002F\u002Fzh-hans.react.dev\u002Freference\u002Freact-dom\u002Fcomponents\u002Fcommon#automatic-batching",[186],"nofollow","自动批处理","）",[131,190,191,194],{},[146,192,193],{},"初始值惰性计算",[146,195,196,199],{},[29,197,198],{},"useState(() => expensiveInit())"," 仅首次渲染执行",[44,201,202],{"id":202},"状态不可变更新",[25,204,205,206,209],{},"对象与数组应创建",[150,207,208],{},"新引用","，勿直接修改原 state：",[48,211,213],{"className":50,"code":212,"language":52,"meta":53,"style":53},"\u002F\u002F 对象\nsetUser((u) => ({ ...u, name: \"新名字\" }));\n\n\u002F\u002F 数组\nsetItems((items) => [...items, newItem]);\nsetItems((items) => items.filter((x) => x.id !== id));\n",[29,214,215,220,225,229,234,239],{"__ignoreMap":53},[57,216,217],{"class":59,"line":60},[57,218,219],{},"\u002F\u002F 对象\n",[57,221,222],{"class":59,"line":66},[57,223,224],{},"setUser((u) => ({ ...u, name: \"新名字\" }));\n",[57,226,227],{"class":59,"line":73},[57,228,70],{"emptyLinePlaceholder":69},[57,230,231],{"class":59,"line":79},[57,232,233],{},"\u002F\u002F 数组\n",[57,235,236],{"class":59,"line":85},[57,237,238],{},"setItems((items) => [...items, newItem]);\n",[57,240,241],{"class":59,"line":90},[57,242,243],{},"setItems((items) => items.filter((x) => x.id !== id));\n",[44,245,35],{"id":246},"usereducer",[25,248,249],{},"适合 state 结构复杂或下一状态依赖多种 action 类型：",[48,251,253],{"className":50,"code":252,"language":52,"meta":53,"style":53},"type State = { count: number };\ntype Action = { type: \"inc\" } | { type: \"dec\" } | { type: \"reset\" };\n\nfunction reducer(state: State, action: Action): State {\n  switch (action.type) {\n    case \"inc\":\n      return { count: state.count + 1 };\n    case \"dec\":\n      return { count: state.count - 1 };\n    case \"reset\":\n      return { count: 0 };\n    default:\n      return state;\n  }\n}\n\nfunction Counter() {\n  const [state, dispatch] = useReducer(reducer, { count: 0 });\n  return (\n    \u003C>\n      \u003Cspan>{state.count}\u003C\u002Fspan>\n      \u003Cbutton type=\"button\" onClick={() => dispatch({ type: \"inc\" })}>\n        +1\n      \u003C\u002Fbutton>\n    \u003C\u002F>\n  );\n}\n",[29,254,255,260,265,269,274,279,284,289,294,299,304,309,315,321,327,332,337,342,348,353,359,365,371,377,383,389,394],{"__ignoreMap":53},[57,256,257],{"class":59,"line":60},[57,258,259],{},"type State = { count: number };\n",[57,261,262],{"class":59,"line":66},[57,263,264],{},"type Action = { type: \"inc\" } | { type: \"dec\" } | { type: \"reset\" };\n",[57,266,267],{"class":59,"line":73},[57,268,70],{"emptyLinePlaceholder":69},[57,270,271],{"class":59,"line":79},[57,272,273],{},"function reducer(state: State, action: Action): State {\n",[57,275,276],{"class":59,"line":85},[57,277,278],{},"  switch (action.type) {\n",[57,280,281],{"class":59,"line":90},[57,282,283],{},"    case \"inc\":\n",[57,285,286],{"class":59,"line":96},[57,287,288],{},"      return { count: state.count + 1 };\n",[57,290,291],{"class":59,"line":102},[57,292,293],{},"    case \"dec\":\n",[57,295,296],{"class":59,"line":108},[57,297,298],{},"      return { count: state.count - 1 };\n",[57,300,301],{"class":59,"line":114},[57,302,303],{},"    case \"reset\":\n",[57,305,306],{"class":59,"line":120},[57,307,308],{},"      return { count: 0 };\n",[57,310,312],{"class":59,"line":311},12,[57,313,314],{},"    default:\n",[57,316,318],{"class":59,"line":317},13,[57,319,320],{},"      return state;\n",[57,322,324],{"class":59,"line":323},14,[57,325,326],{},"  }\n",[57,328,330],{"class":59,"line":329},15,[57,331,123],{},[57,333,335],{"class":59,"line":334},16,[57,336,70],{"emptyLinePlaceholder":69},[57,338,340],{"class":59,"line":339},17,[57,341,76],{},[57,343,345],{"class":59,"line":344},18,[57,346,347],{},"  const [state, dispatch] = useReducer(reducer, { count: 0 });\n",[57,349,351],{"class":59,"line":350},19,[57,352,93],{},[57,354,356],{"class":59,"line":355},20,[57,357,358],{},"    \u003C>\n",[57,360,362],{"class":59,"line":361},21,[57,363,364],{},"      \u003Cspan>{state.count}\u003C\u002Fspan>\n",[57,366,368],{"class":59,"line":367},22,[57,369,370],{},"      \u003Cbutton type=\"button\" onClick={() => dispatch({ type: \"inc\" })}>\n",[57,372,374],{"class":59,"line":373},23,[57,375,376],{},"        +1\n",[57,378,380],{"class":59,"line":379},24,[57,381,382],{},"      \u003C\u002Fbutton>\n",[57,384,386],{"class":59,"line":385},25,[57,387,388],{},"    \u003C\u002F>\n",[57,390,392],{"class":59,"line":391},26,[57,393,117],{},[57,395,397],{"class":59,"line":396},27,[57,398,123],{},[25,400,401,406,407,42],{},[38,402,405],{"href":403,"rel":404},"https:\u002F\u002Fredux-toolkit.js.org\u002F",[186],"Redux Toolkit"," 的 reducer 风格与此类似，见 ",[38,408,410],{"href":409},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fstate","状态管理",[44,412,414],{"id":413},"hooks-规则","Hooks 规则",[416,417,418,430],"ol",{},[419,420,421,422,425,426,429],"li",{},"只在",[150,423,424],{},"函数组件顶层","或",[150,427,428],{},"自定义 Hook 顶层","调用，勿放在条件、循环内。",[419,431,432],{},"只从 React 函数或自定义 Hook 中调用 Hooks。",[25,434,435,436,441],{},"违反规则会导致 Hook 调用顺序错乱。可用 ",[38,437,440],{"href":438,"rel":439},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Feslint-plugin-react-hooks",[186],"eslint-plugin-react-hooks"," 静态检查。",[44,443,444],{"id":444},"参考文献",[25,446,447],{},"以下链接在编写时均可正常访问：",[125,449,450,459],{},[128,451,452],{},[131,453,454,457],{},[134,455,456],{},"资料",[134,458,139],{},[141,460,461,473,484],{},[131,462,463,470],{},[146,464,465],{},[38,466,469],{"href":467,"rel":468},"https:\u002F\u002Fzh-hans.react.dev\u002Freference\u002Freact\u002FuseState",[186],"React：useState",[146,471,472],{},"API",[131,474,475,482],{},[146,476,477],{},[38,478,481],{"href":479,"rel":480},"https:\u002F\u002Fzh-hans.react.dev\u002Freference\u002Freact\u002FuseReducer",[186],"React：useReducer",[146,483,472],{},[131,485,486,493],{},[146,487,488],{},[38,489,492],{"href":490,"rel":491},"https:\u002F\u002Fzh-hans.react.dev\u002Flearn\u002Fstate-as-a-snapshot",[186],"React：State 作为快照",[146,494,495],{},"渲染与 state",[497,498,499],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":53,"searchDepth":66,"depth":66,"links":501},[502,503,504,505,506],{"id":46,"depth":66,"text":31},{"id":202,"depth":66,"text":202},{"id":246,"depth":66,"text":35},{"id":413,"depth":66,"text":414},{"id":444,"depth":66,"text":444},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fframe\u002FReact\u002Fhooks\u002Fstate","knowledge\u002Fknowledge\u002Fbooklet\u002Fframe\u002FReact\u002Fhooks\u002Fstate.md","md",{"items":512},[513,517,521,525,527,531],{"path":514,"title":515,"description":516,"category":10,"score":108},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Feffect","Effect 与副作用","useEffect 用于在组件渲染后执行与外部系统同步的逻辑（请求、订阅、手动改 DOM 等）。涵盖基本用法、依赖数组、适用\u002F不适用场景、useLayoutEffect、最佳实践与常见坑。",{"path":518,"title":519,"description":520,"category":10,"score":108},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fcontext","Context","Context 在组件树中跨层级传递数据，避免 props 逐层透传。涵盖创建与使用、自定义 Hook 封装、性能拆分、与全局状态库对照、最佳实践与常见坑。",{"path":522,"title":523,"description":524,"category":10,"score":108},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fhooks\u002Fmemo","Memo、Callback 与 Ref","本文介绍 useRef、useMemo、useCallback、memo 及自定义 Hook。见 Effect。",{"path":409,"title":410,"description":526,"category":10,"score":96},"React 组件内 state 用 Hooks；跨组件共享状态可选 Context、Redux Toolkit、Zustand 等。见 Context Hook。",{"path":528,"title":529,"description":530,"category":10,"score":79},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Frendering","渲染与协调","React 将组件渲染为虚拟 DOM 树，再协调（reconciliation）到浏览器 DOM。理解该过程有助于解释 key、memo 与性能优化。涵盖渲染流程、Fiber、startTransition、Suspense、最佳实践与常见坑。",{"path":532,"title":533,"description":534,"category":10,"score":79},"\u002Fknowledge\u002Fbooklet\u002Fframe\u002Freact\u002Fproject","工程化","新建 React 项目时，官方与社区主流工具为 Vite；全栈场景常用 Next.js。涵盖 Vite + React、Create React App 现状、Next.js、目录结构、环境变量、部署、最佳实践与常见坑。",{"series":536,"title":13,"items":537,"index":73,"prev":545,"next":546},"booklet\u002Fframe\u002Freact\u002Fhooks",[538,540,542,544],{"path":518,"title":519,"date":539,"pinned":16},"2021-10-16",{"path":514,"title":515,"date":541,"pinned":16},"2024-10-11",{"path":522,"title":523,"date":543,"pinned":16},"2021-02-28",{"path":7,"title":8,"date":15,"pinned":16},{"path":522,"title":523,"date":543,"pinned":16},null]