[{"data":1,"prerenderedAt":263},["ShallowReactive",2],{"article-react-use-state-practice":3,"related-articles":162},{"id":4,"title":5,"body":6,"categoryLabel":148,"categorySlug":149,"description":150,"extension":151,"featured":152,"meta":153,"navigation":74,"path":154,"publishedAt":155,"readingMinutes":90,"seo":156,"stem":157,"tags":158,"__hash__":161},"blog\u002Fblog\u002Freact-use-state-practice.md","用 useState 管理一个简单的计数交互",{"type":7,"value":8,"toc":144},"minimark",[9,13,17,124,127,130,137,140],[10,11,12],"p",{},"计数器是观察 React 更新流程的小练习。组件第一次渲染时创建 state，用户点击按钮后更新 state，React 再根据新值刷新界面。",[14,15,16],"h2",{"id":16},"声明和更新状态",[18,19,24],"pre",{"className":20,"code":21,"language":22,"meta":23,"style":23},"language-tsx shiki shiki-themes github-light-high-contrast","const [count, setCount] = useState(0)\n\nfunction increase() {\n  setCount((current) => current + 1)\n}\n","tsx","",[25,26,27,69,76,88,118],"code",{"__ignoreMap":23},[28,29,32,36,40,44,47,50,53,56,60,63,66],"span",{"class":30,"line":31},"line",1,[28,33,35],{"class":34},"sTJeM","const",[28,37,39],{"class":38},"sigWx"," [",[28,41,43],{"class":42},"sacAq","count",[28,45,46],{"class":38},", ",[28,48,49],{"class":42},"setCount",[28,51,52],{"class":38},"] ",[28,54,55],{"class":34},"=",[28,57,59],{"class":58},"s3dhs"," useState",[28,61,62],{"class":38},"(",[28,64,65],{"class":42},"0",[28,67,68],{"class":38},")\n",[28,70,72],{"class":30,"line":71},2,[28,73,75],{"emptyLinePlaceholder":74},true,"\n",[28,77,79,82,85],{"class":30,"line":78},3,[28,80,81],{"class":34},"function",[28,83,84],{"class":58}," increase",[28,86,87],{"class":38},"() {\n",[28,89,91,94,97,101,104,107,110,113,116],{"class":30,"line":90},4,[28,92,93],{"class":58},"  setCount",[28,95,96],{"class":38},"((",[28,98,100],{"class":99},"sV4o_","current",[28,102,103],{"class":38},") ",[28,105,106],{"class":34},"=>",[28,108,109],{"class":38}," current ",[28,111,112],{"class":34},"+",[28,114,115],{"class":42}," 1",[28,117,68],{"class":38},[28,119,121],{"class":30,"line":120},5,[28,122,123],{"class":38},"}\n",[10,125,126],{},"当新值依赖旧值时，使用函数形式的更新更清楚，也能避免连续更新时读取到旧值。",[14,128,129],{"id":129},"让操作有清楚的反馈",[10,131,132,133,136],{},"按钮文案应描述操作，计数值可以用文本展示。重置按钮则把状态恢复到初始值：",[25,134,135],{},"setCount(0)","。",[10,138,139],{},"这个练习也适合检查按钮的键盘操作、焦点样式和窄屏下的间距，不只是确认数字会变化。",[141,142,143],"style",{},"html pre.shiki code .sTJeM, html code.shiki .sTJeM{--shiki-default:#A0111F}html pre.shiki code .sigWx, html code.shiki .sigWx{--shiki-default:#0E1116}html pre.shiki code .sacAq, html code.shiki .sacAq{--shiki-default:#023B95}html pre.shiki code .s3dhs, html code.shiki .s3dhs{--shiki-default:#622CBC}html pre.shiki code .sV4o_, html code.shiki .sV4o_{--shiki-default:#702C00}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);}",{"title":23,"searchDepth":71,"depth":71,"links":145},[146,147],{"id":16,"depth":71,"text":16},{"id":129,"depth":71,"text":129},"React","react","从加一和重置两个操作，熟悉 React state 的声明与更新方式。","md",false,{},"\u002Fblog\u002Freact-use-state-practice","2026-07-30",{"title":5,"description":150},"blog\u002Freact-use-state-practice",[148,159,160],"useState","练习记录","qp2CcWABTkRt5iXi0z0wBw33pKjubEO2CU-m7LWFFAk",[163,175,184,194,202,210,217,225,227,237,245,253],{"id":164,"path":165,"title":166,"description":167,"categorySlug":168,"categoryLabel":169,"publishedAt":170,"readingMinutes":90,"featured":74,"tags":171},"blog\u002Fblog\u002Fcss-grid-cards.md","\u002Fblog\u002Fcss-grid-cards","用 CSS Grid 排列自适应卡片","使用 repeat、minmax 和 auto-fit，让卡片列数随容器宽度自然变化。","styles","页面与样式","2026-09-10",[172,173,174],"CSS","Grid","页面布局",{"id":176,"path":177,"title":178,"description":179,"categorySlug":168,"categoryLabel":169,"publishedAt":180,"readingMinutes":120,"featured":152,"tags":181},"blog\u002Fblog\u002Fmobile-layout-checklist.md","\u002Fblog\u002Fmobile-layout-checklist","做移动端页面适配时的检查笔记","从内容宽度、触控区域、文字换行和键盘焦点几个方面检查窄屏体验。","2026-08-20",[172,182,183],"响应式","可访问性",{"id":185,"path":186,"title":187,"description":188,"categorySlug":149,"categoryLabel":148,"publishedAt":189,"readingMinutes":190,"featured":74,"tags":191},"blog\u002Fblog\u002Freact-controlled-form.md","\u002Fblog\u002Freact-controlled-form","React 受控表单：从一个输入框开始","用一个简单的姓名输入框，理解 value、onChange 与 React state 之间的关系。","2026-09-18",6,[148,192,193],"表单","学习笔记",{"id":195,"path":196,"title":197,"description":198,"categorySlug":149,"categoryLabel":148,"publishedAt":199,"readingMinutes":90,"featured":152,"tags":200},"blog\u002Fblog\u002Freact-list-keys.md","\u002Fblog\u002Freact-list-keys","React 列表渲染里的 key 有什么用","通过稳定的 key 帮助 React 识别列表项目，避免用数组下标造成的状态错位。","2026-08-12",[148,201,193],"列表",{"id":203,"path":204,"title":205,"description":206,"categorySlug":149,"categoryLabel":148,"publishedAt":207,"readingMinutes":120,"featured":152,"tags":208},"blog\u002Fblog\u002Freact-props-and-state.md","\u002Fblog\u002Freact-props-and-state","React 组件里的 Props 和 State","用一个简单的信息卡片区分外部传入的数据和组件内部会变化的数据。","2026-08-28",[148,209,193],"组件",{"id":211,"path":212,"title":213,"description":214,"categorySlug":149,"categoryLabel":148,"publishedAt":215,"readingMinutes":120,"featured":152,"tags":216},"blog\u002Fblog\u002Freact-small-components.md","\u002Fblog\u002Freact-small-components","把重复的界面拆成小组件","从重复的按钮和卡片开始，认识组件拆分的几个简单信号。","2026-07-16",[148,209,160],{"id":218,"path":219,"title":220,"description":221,"categorySlug":149,"categoryLabel":148,"publishedAt":222,"readingMinutes":190,"featured":152,"tags":223},"blog\u002Fblog\u002Freact-use-effect-notes.md","\u002Fblog\u002Freact-use-effect-notes","React useEffect 的依赖和清理","整理 useEffect 何时运行，以及订阅和定时器为什么需要清理。","2026-06-30",[148,224,193],"useEffect",{"id":4,"path":154,"title":5,"description":150,"categorySlug":149,"categoryLabel":148,"publishedAt":155,"readingMinutes":90,"featured":152,"tags":226},[148,159,160],{"id":228,"path":229,"title":230,"description":231,"categorySlug":232,"categoryLabel":233,"publishedAt":234,"readingMinutes":120,"featured":74,"tags":235},"blog\u002Fblog\u002Ftypescript-component-props.md","\u002Fblog\u002Ftypescript-component-props","给 React 组件的 Props 添加 TypeScript 类型","用必填属性和可选属性描述组件接口，让使用方式在编辑器里更清晰。","typescript","TypeScript","2026-09-02",[233,148,236],"类型",{"id":238,"path":239,"title":240,"description":241,"categorySlug":232,"categoryLabel":233,"publishedAt":242,"readingMinutes":120,"featured":74,"tags":243},"blog\u002Fblog\u002Ftypescript-narrowing.md","\u002Fblog\u002Ftypescript-narrowing","TypeScript 类型收窄入门","使用 typeof 和条件判断缩小联合类型的范围，让后续代码更安全。","2026-09-25",[233,244,193],"类型收窄",{"id":246,"path":247,"title":248,"description":249,"categorySlug":232,"categoryLabel":233,"publishedAt":250,"readingMinutes":90,"featured":152,"tags":251},"blog\u002Fblog\u002Ftypescript-union-status.md","\u002Fblog\u002Ftypescript-union-status","用联合类型描述请求状态","把加载、成功和失败等状态限制在明确的集合里，减少拼写错误和遗漏分支。","2026-08-04",[233,252,193],"联合类型",{"id":254,"path":255,"title":256,"description":257,"categorySlug":258,"categoryLabel":259,"publishedAt":260,"readingMinutes":90,"featured":152,"tags":261},"blog\u002Fblog\u002Fvite-react-project.md","\u002Fblog\u002Fvite-react-project","用 Vite 创建一个 React + TypeScript 小项目","记录从初始化模板到启动开发服务器的基础步骤，以及几个值得先检查的文件。","tools","开发工具","2026-06-12",[262,148,233],"Vite",1792674168662]