React 的数据传递流程
在 React 中,数据传递通常是自上而下的,也就是父组件把数据通过 props
传递给子组件,子组件无法直接修改父组件的数据。
例子:父组件向子组件传递数据
const Parent = () => {
const user = { name: "John", age: 24 };
return <Child user={user} />;
};
const Child = ({ user }) => {
return (
<div>
<h2>姓名: {user.name}</h2>
<h2>年龄: {user.age}</h2>
</div>
);
};
问题不大,看起来很简单,对吧?
子组件传递数据的“噩梦”
但如果子组件需要修改父组件的数据,就必须通过回调函数,让子组件把新的数据“反向”传回父组件,让父组件更新数据。
例子:子组件修改父组件数据
const Parent = () => {
const [user, setUser] = useState({ name: "John", age: 24 });
// 让子组件调用这个方法来修改 user
const updateUser = (newName) => {
setUser({ ...user, name: newName });
};
return <Child user={user} updateUser={updateUser} />;
};
const Child = ({ user, updateUser }) => {
return (
<div>
<h2>姓名: {user.name}</h2>
<button onClick={() => updateUser("Alice")}>改名为 Alice</button>
</div>
);
};
这样,点击按钮后,John
就会变成 Alice
,但这还不算太糟糕。
真正的问题是:如果有很多层嵌套怎么办?
多层嵌套时的“数据传递地狱”
如果 Child
不是直接在 Parent
里面,而是嵌套了好几层,每一层都要手动传 props
,就会变得非常痛苦。
数据传递“地狱”示例
const Parent = () => {
const [user, setUser] = useState({ name: "John", age: 24 });
const updateUser = (newName) => {
setUser({ ...user, name: newName });
};
return <Level1 user={user} updateUser={updateUser} />;
};
// 一层又一层地传递 props...
const Level1 = ({ user, updateUser }) => <Level2 user={user} updateUser={updateUser} />;
const Level2 = ({ user, updateUser }) => <Level3 user={user} updateUser={updateUser} />;
const Level3 = ({ user, updateUser }) => <Level4 user={user} updateUser={updateUser} />;
const Level4 = ({ user, updateUser }) => (
<div>
<h2>姓名: {user.name}</h2>
<button onClick={() => updateUser("Alice")}>改名为 Alice</button>
</div>
);
问题:
- 你要在 每一层组件 都写
props
传递,代码变得冗长且难以维护。 - 组件越多,数据传递越混乱,很容易出错。
- 这个问题被称为 “Props Drilling”(属性挖掘),就像挖矿一样,数据要一层一层往下挖。
如何解决?—— useContext
来救场!
React 提供了 useContext
,它就像一个全局数据仓库,可以让任何组件直接访问数据,而不需要层层 props
传递。
步骤 1:创建 Context
import { createContext, useState } from "react";
// 1. 创建 Context
const UserContext = createContext();
// 2. 创建 Provider 组件
const UserProvider = ({ children }) => {
const [user, setUser] = useState({ name: "John", age: 24 });
const updateUser = (newName) => {
setUser({ ...user, name: newName });
};
return (
<UserContext.Provider value={{ user, updateUser }}>
{children} {/* 这里的 children 让所有子组件都能访问这个 Context */}
</UserContext.Provider>
);
};
export { UserContext, UserProvider };
步骤 2:子组件直接用 useContext
读取数据
import { useContext } from "react";
import { UserContext } from "./UserContext";
const UserProfile = () => {
const { user, updateUser } = useContext(UserContext);
return (
<div>
<h2>姓名: {user.name}</h2>
<button onClick={() => updateUser("Alice")}>改名为 Alice</button>
</div>
);
};
export default UserProfile;
步骤 3:在 App.js
里包裹 Provider
import React from "react";
import { UserProvider } from "./UserContext";
import UserProfile from "./UserProfile";
const App = () => {
return (
<UserProvider>
<UserProfile />
</UserProvider>
);
};
export default App;
为什么 useContext
很强大?
- 避免了“数据传递地狱”:不需要层层
props
传递,所有组件都能直接访问数据。 - 代码更清晰:不管组件嵌套多少层,都能方便地读取和更新数据。
- 性能更好:不会因为
props
变化导致所有中间组件都重新渲染。
总结
传统数据传递方式(
props
)- 适合小型项目,数据传递简单时使用。
- 但是当层级变深时,
props drilling
让代码变得难以维护。
useContext
方式- 适合共享状态的场景,比如用户信息、主题设置、语言切换等。
- 让所有组件都能直接访问数据,避免
props
层层传递。
最佳实践
- 如果数据只在父子组件之间传递,用
props
即可。 - 如果数据需要被多个组件共享,使用
useContext
来简化代码。
- 如果数据只在父子组件之间传递,用
🚀 现在,你可以摆脱“数据传递地狱”,用 useContext
让 React 代码更清爽!