当前位置: 首页 > news >正文

03-状态管理与路由——01. useState + Props - 状态提升

01. useState + Props - 状态提升

一、5W1H 概述

维度内容
What将多个组件共享的状态提升到最近的共同父组件中
Why实现兄弟组件间的数据共享和同步
When多个组件需要共享同一状态、兄弟组件通信
Where共同父组件中定义状态,通过 props 传递给子组件
Who需要实现简单状态共享的开发者
How父组件使用 useState,通过 props 传递状态和更新函数

二、What - 什么是状态提升?

状态提升(Lifting State Up)是 React 中的一种模式,当多个组件需要共享同一状态时,将该状态提升到它们的共同父组件中,然后通过 props 传递给子组件。

// ❌ 状态在各自组件中,无法共享 function ComponentA() { const [data, setData] = useState(''); } function ComponentB() { const [data, setData] = useState(''); } // ✅ 状态提升到父组件 function Parent() { const [data, setData] = useState(''); return ( <> <ComponentA data={data} setData={setData} /> <ComponentB data={data} setData={setData} /> </> ); }

三、Why - 为什么需要状态提升?

3.1 解决兄弟组件通信问题

在 React 中,数据是单向流动的。兄弟组件之间无法直接共享状态,需要通过父组件中转。

3.2 保持数据一致性

将状态提升后,所有子组件都使用同一份数据,确保数据同步。

3.3 简化状态管理

对于简单的父子/兄弟通信,状态提升是最直接、最简单的方案。


四、When - 何时使用状态提升?

场景是否使用说明
兄弟组件需要共享状态✅ 推荐最直接的解决方案
父子组件需要共享状态✅ 推荐原生 props 传递
跨多层组件共享状态❌ 不推荐会导致 props drilling
全局状态❌ 不推荐使用 Context 或状态管理库

五、Where - 在哪里使用?

  • 共同父组件中定义状态
  • 子组件中通过 props 接收状态和更新函数
// 父组件 function Parent() { const [sharedState, setSharedState] = useState(initialValue); return <Child state={sharedState} setState={setSharedState} />; } // 子组件 function Child({ state, setState }) { return <button onClick={() => setState(newValue)}>{state}</button>; }

六、Who - 谁需要使用?

所有需要实现组件间状态共享的 React 开发者。


七、How - 如何使用状态提升?

7.1 基础示例:兄弟组件通信

// 父组件 function Parent() { const [sharedData, setSharedData] = useState(''); return ( <div> <ChildA sharedData={sharedData} setSharedData={setSharedData} /> <ChildB sharedData={sharedData} /> </div> ); } // 子组件 A:可以修改状态 function ChildA({ sharedData, setSharedData }) { return ( <div> <input value={sharedData} onChange={(e) => setSharedData(e.target.value)} placeholder="输入内容..." /> </div> ); } // 子组件 B:只读取状态 function ChildB({ sharedData }) { return <div>接收到的数据: {sharedData}</div>; }

7.2 计数器示例

function CounterParent() { const [count, setCount] = useState(0); return ( <div> <CounterDisplay count={count} /> <CounterControls setCount={setCount} /> </div> ); } function CounterDisplay({ count }) { return <h1>当前计数: {count}</h1>; } function CounterControls({ setCount }) { return ( <div> <button onClick={() => setCount(prev => prev + 1)}>+1</button> <button onClick={() => setCount(prev => prev - 1)}>-1</button> <button onClick={() => setCount(0)}>重置</button> </div> ); }

7.3 表单示例

function FormParent() { const [formData, setFormData] = useState({ username: '', email: '', age: '' }); const handleChange = (field, value) => { setFormData(prev => ({ ...prev, [field]: value })); }; return ( <div> <FormInput label="用户名" value={formData.username} onChange={(value) => handleChange('username', value)} /> <FormInput label="邮箱" value={formData.email} onChange={(value) => handleChange('email', value)} /> <FormInput label="年龄" value={formData.age} onChange={(value) => handleChange('age', value)} /> <FormPreview data={formData} /> </div> ); } function FormInput({ label, value, onChange }) { return ( <div> <label>{label}:</label> <input value={value} onChange={(e) => onChange(e.target.value)} /> </div> ); } function FormPreview({ data }) { return ( <div> <h3>预览</h3> <p>用户名: {data.username}</p> <p>邮箱: {data.email}</p> <p>年龄: {data.age}</p> </div> ); }

7.4 Todo 列表示例

function TodoApp() { const [todos, setTodos] = useState([ { id: 1, text: '学习 React', completed: false }, { id: 2, text: '学习状态提升', completed: false } ]); const addTodo = (text) => { setTodos(prev => [...prev, { id: Date.now(), text, completed: false }]); }; const toggleTodo = (id) => { setTodos(prev => prev.map(todo => todo.id === id ? { ...todo, completed: !todo.completed } : todo ) ); }; const deleteTodo = (id) => { setTodos(prev => prev.filter(todo => todo.id !== id)); }; return ( <div> <TodoInput onAdd={addTodo} /> <TodoList todos={todos} onToggle={toggleTodo} onDelete={deleteTodo} /> <TodoStats todos={todos} /> </div> ); } function TodoInput({ onAdd }) { const [text, setText] = useState(''); const handleSubmit = (e) => { e.preventDefault(); if (text.trim()) { onAdd(text); setText(''); } }; return ( <form onSubmit={handleSubmit}> <input value={text} onChange={(e) => setText(e.target.value)} /> <button type="submit">添加</button> </form> ); } function TodoList({ todos, onToggle, onDelete }) { return ( <ul> {todos.map(todo => ( <li key={todo.id}> <input type="checkbox" checked={todo.completed} onChange={() => onToggle(todo.id)} /> <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text} </span> <button onClick={() => onDelete(todo.id)}>删除</button> </li> ))} </ul> ); } function TodoStats({ todos }) { const total = todos.length; const completed = todos.filter(t => t.completed).length; const active = total - completed; return ( <div> <p>总计: {total} | 已完成: {completed} | 未完成: {active}</p> </div> ); }

7.5 购物车示例

function CartApp() { const [cart, setCart] = useState([]); const addToCart = (product) => { setCart(prev => { const existing = prev.find(item => item.id === product.id); if (existing) { return prev.map(item => item.id === product.id ? { ...item, quantity: item.quantity + 1 } : item ); } return [...prev, { ...product, quantity: 1 }]; }); }; const updateQuantity = (id, quantity) => { setCart(prev => prev.map(item => item.id === id ? { ...item, quantity: Math.max(0, quantity) } : item ).filter(item => item.quantity > 0) ); }; const totalPrice = cart.reduce((sum, item) => sum + item.price * item.quantity, 0); return ( <div> <ProductList onAddToCart={addToCart} /> <Cart cart={cart} onUpdateQuantity={updateQuantity} /> <CartTotal totalPrice={totalPrice} /> </div> ); } function ProductList({ onAddToCart }) { const products = [ { id: 1, name: '商品 A', price: 100 }, { id: 2, name: '商品 B', price: 200 }, { id: 3, name: '商品 C', price: 150 } ]; return ( <div> {products.map(product => ( <div key={product.id}> <span>{product.name} - ¥{product.price}</span> <button onClick={() => onAddToCart(product)}>加入购物车</button> </div> ))} </div> ); } function Cart({ cart, onUpdateQuantity }) { if (cart.length === 0) { return <p>购物车是空的</p>; } return ( <ul> {cart.map(item => ( <li key={item.id}> {item.name} - ¥{item.price} x {item.quantity} <button onClick={() => onUpdateQuantity(item.id, item.quantity + 1)}>+</button> <button onClick={() => onUpdateQuantity(item.id, item.quantity - 1)}>-</button> </li> ))} </ul> ); } function CartTotal({ totalPrice }) { return <h3>总计: ¥{totalPrice}</h3>; }

八、状态提升 vs Props Drilling

// ❌ 不好的做法:过度提升导致 props drilling function App() { const [user, setUser] = useState(null); return ( <Header user={user} /> <Main user={user} setUser={setUser} /> <Footer user={user} /> ); } // ✅ 好的做法:只提升必要的状态 function App() { return ( <Header /> <UserSection /> {/* 用户状态只在这里使用 */} <Footer /> ); } function UserSection() { const [user, setUser] = useState(null); return <Profile user={user} setUser={setUser} />; }

九、常见陷阱

9.1 过度提升

// ❌ 将不需要共享的状态也提升了 function Parent() { const [count, setCount] = useState(0); const [text, setText] = useState(''); // 只在 ChildA 中使用 return ( <> <ChildA count={count} setCount={setCount} text={text} setText={setText} /> <ChildB count={count} /> </> ); } // ✅ 只提升共享的状态 function Parent() { const [count, setCount] = useState(0); return ( <> <ChildA count={count} setCount={setCount} /> <ChildB count={count} /> </> ); }

9.2 忘记使用函数式更新

// ❌ 可能导致过期状态问题 function Parent() { const [count, setCount] = useState(0); const incrementTwice = () => { setCount(count + 1); setCount(count + 1); // 只增加 1 }; return <Child incrementTwice={incrementTwice} />; } // ✅ 使用函数式更新 function Parent() { const [count, setCount] = useState(0); const incrementTwice = () => { setCount(prev => prev + 1); setCount(prev => prev + 1); // 增加 2 }; return <Child incrementTwice={incrementTwice} />; }

十、练习题

基础题

  1. 实现一个温度转换器:摄氏度和华氏度互相转换
  2. 实现一个简单的计算器:两个数字输入,显示计算结果

进阶题

  1. 实现一个可编辑的表格:支持添加、删除、编辑行

十一、小结

要点说明
适用场景兄弟组件通信、简单状态共享
实现方式父组件 useState + props 传递
优点简单直接,无需额外依赖
缺点深层传递会导致 props drilling

http://www.jsqmd.com/news/1023181/

相关文章:

  • ReWOO推理框架:解耦思考与感知的工业级大模型架构
  • 2026年浙江隔音窗全屋改造选购宝典:杭州静音门窗品牌深度对比 - 企业名录优选推荐
  • selenium的定位方式java版
  • 终极指南:如何用Legacy-iOS-Kit让你的旧iPhone重获新生
  • 35岁网安工程师奉劝那些打算去IT的人,别乱吃苦
  • 从Redmon看监控系统设计:轻量级、低侵入的Sidekiq队列监控实践
  • Rhino.Inside Revit几何体导入终极指南:5个实用技巧解决常见失败问题
  • FPGA实战(15):基于 Xilinx CORDIC IP 核的坐标变换模块设计与仿真
  • Mac原生集成ChatGPT:零代码实现系统级AI助手
  • 2026最新淮安市黄金回收价格一览表 避坑与商家推荐 - 润富黄金回收
  • 义乌直发物流专线四家企业服务能力对比哪家好 - 奔跑123
  • 编写程序统计睡前手机时长,内容类型,分析对入睡速度,睡眠质量的影响。
  • 3大核心技术突破:Wand-Enhancer如何重塑本地游戏增强体验
  • 2026最新国内以及河北地区四氟垫片 / 膨体四氟垫片生产厂家实力排行及采购指南 - 奔跑123
  • 避坑指南:如何将Simulink模型导出为FMU文件供Amesim调用(解决步长报错)
  • NC | 单细胞分析揭示头颈部癌早期转移过程中潜在的免疫逃逸机制(R语言版本)
  • 如何快速打造专业级Qt界面:Qt Material主题库的完整使用指南
  • 毕业论文神器!2026年性价比拉满的专业一键生成论文工具
  • 终极指南:5步彻底解决ComfyUI ControlNet Aux预处理节点加载失败问题
  • Matplotlib标注思维:让图表具备AI级认知引导能力
  • 2026年6月工程车辆计数软件可靠之选:前沿AI技术如何重塑土石方管理 - 热点速览
  • 阿里云Qoder:1天上线Agent背后的Serverless架构与商业化逻辑
  • 从手动操作到智能自动化:淘金币脚本如何为你每天节省20分钟
  • 预测性线索评分实战:从逻辑回归到CRM落地的完整链路
  • NVIDIA Profile Inspector深度系统优化指南:5大高级配置方案解决显卡性能瓶颈
  • FPGA实战(16):RLS自适应滤波器的Verilog实现与FPGA设计详解
  • CDR转PDF:设计师必备的印刷级文件转换与质检全流程
  • 零基础视频格式调整全套教学,无损转码保留画质与完整原声内容 - 软件工具教程方法
  • 前端技术16-Redux太复杂?从Redux到Zustand:我们的状态管理代码减少了70%,极简API、零样板代码的状态管理方案
  • 2026年 广东保安公司推荐榜单:专业安防与贴心服务口碑之选 - 品牌发掘