news 2026/6/25 12:28:32

04-性能优化与最佳实践——07. 状态提升 - 避免 Prop Drilling

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
04-性能优化与最佳实践——07. 状态提升 - 避免 Prop Drilling

07. 状态提升 - 避免 Prop Drilling

一、5W1H 概述

维度内容
What将共享状态提升到共同父组件,通过 props 传递
Why实现兄弟组件通信,避免 props 层层传递
When多个组件需要共享状态、兄弟组件通信
Where共同父组件中定义状态
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} /> </> ); }

三、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> ); } function ChildA({ sharedData, setSharedData }) { return ( <div> <input value={sharedData} onChange={(e) => setSharedData(e.target.value)} placeholder="输入内容..." /> </div> ); } 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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/25 12:28:02

蓝奏云文件直链获取:告别繁琐下载流程的技术方案

蓝奏云文件直链获取&#xff1a;告别繁琐下载流程的技术方案 【免费下载链接】LanzouAPI 蓝奏云直链&#xff0c;蓝奏api&#xff0c;蓝奏解析&#xff0c;蓝奏云解析API&#xff0c;蓝奏云带密码解析 项目地址: https://gitcode.com/gh_mirrors/la/LanzouAPI 你是否曾经…

作者头像 李华
网站建设 2026/6/25 12:26:19

遗传算法工业落地避坑指南:适应度设计、早熟防治与收敛诊断

1. 项目概述&#xff1a;为什么“遗传算法第二讲”比第一讲更值得你花时间重读“遗传算法”这四个字&#xff0c;十年前在高校课堂里是《人工智能导论》最后一章的冷门配角&#xff0c;五年后成了算法岗面试必问的“经典老题”&#xff0c;而今天——它已经悄悄长进了工业级推荐…

作者头像 李华
网站建设 2026/6/25 12:25:50

孟献贵民法精讲pdf|孟献贵民法视频|孟献贵民法口诀

孟献贵民法精讲pdf|孟献贵民法视频|孟献贵民法口诀资料全科都有孟献贵民法口诀 PDFhttps://tool.nineya.com/s/1jr0lk22ev 【语文真题】1. 下列词语中&#xff0c;书写正确的一项是&#xff08; &#xff09; A. 迫不及待 B. 迫不急待 C. 迫不既待 D. 迫不及带 答案&#xff1a…

作者头像 李华
网站建设 2026/6/25 12:22:06

多项式插值实战:拉格朗日法在嵌入式温度补偿中的工程落地

1. 什么是多项式插值&#xff1a;从“画一条光滑曲线”开始的真实需求你有没有遇到过这样的场景&#xff1a;手头只有几个离散的实验数据点——比如某材料在0℃、25℃、60℃、100℃下的热膨胀系数&#xff0c;但你需要知道它在42℃或78.3℃时的精确值&#xff1b;又或者你在做传…

作者头像 李华
网站建设 2026/6/25 12:20:33

minio对象存储代码思路

好的&#xff0c;我们来详细探讨一个通用的**对象存储&#xff08;基于本地磁盘实现&#xff09;**的代码设计思路。这种设计与MinIO的核心思想类似&#xff0c;但会更专注于基础实现&#xff0c;便于理解其工作原理。&#x1f329;️一、系统设计与组件拆分1. 核心组件(1) 存储…

作者头像 李华
网站建设 2026/6/25 12:19:49

绕过Cloudflare挑战实现ChatGPT自动化访问的技术方案与实践

1. 项目概述与核心挑战最近在折腾ChatGPT自动化项目时&#xff0c;很多朋友都卡在了同一个地方&#xff1a;Cloudflare的挑战页。当你试图用脚本或程序去访问ChatGPT官网时&#xff0c;经常会弹出一个“正在验证浏览器”的页面&#xff0c;或者要求你点击一个“我不是机器人”的…

作者头像 李华