介绍
在 React 中,数据通过 props(父组件到子组件)从上到下流动是很常见的,但这并不总是理想的。在某些情况下,您的数据需要可用于不同深度的许多嵌套组件,并且传递道具变得难以维护且容易出错。
在这种情况下,使用Redux来管理状态似乎是个好主意,但许多人认为它不应该是您的首选。
React Context是跨组件共享数据的替代解决方案,而无需在每个级别手动传递 props。
在本文中,您将探索 Context API 并了解如何使用它来管理用户状态。
先决条件
要阅读本文,您需要:
本教程已通过 Node v15.3.0、npmv6.14.9 和reactv17.0.1 验证。
了解问题
下面是一个Page传递 auser和avatarSizeprop的组件示例:
<Page user={user} avatarSize={avatarSize} />
它呈现一个PageLayout组件:
<PageLayout user={user} avatarSize={avatarSize} />
它呈现一个NavigationBar组件:
<NavigationBar user={user} avatarSize={avatarSize} />
渲染 aLink并且Avatar使用userandavatarSize道具:
<Link href={user.permalink}>
<Avatar user={user} size={avatarSize} />
</Link>
在上面的例子中,只有Avatar组件实际使用了userprop。然而,它的每个祖先组件(父、祖父等)都会接收user并传递它。这意味着如果Avatar组件将来需要另一个 prop,你必须确保它的每个祖先组件接收并传递它。
实际上,user状态将需要在许多不同的组件之间共享,因此将它作为 prop 传递将导致它比上面的示例嵌套更深。
创造 React.createContext
该React.createContext方法返回一个Context对象。这个Context对象带有两个重要的 React 组件,允许订阅数据:Provider和Consumer.
当 React 渲染一个订阅这个Context对象的组件时,它会从Provider树中它上面最接近的匹配组件读取当前上下文值。
该createContext方法接受一个可选defaultValue参数,Context.Consumer如果Context.Provider在树中找不到匹配的组件,则会提供该参数。
创建一个Context对象并将其导出以供其他组件使用:
import React from 'react';
const userContext = React.createContext({user: {}});
export { userContext };
在上面的示例中,您初始化userContext并提供defaultValue了{user: {}}.
现在您有了一个Context对象,您可以为它提供一个值并订阅更改。
申请 Context.Provider
将用户状态作为值传递给,context.Provider以便它可以被以下使用context.Consumer:
import React from 'react';
import Main from './Main';
import {userContext} from './userContext';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {}
};
}
componentDidMount() {
// get and set currently logged in user to state
}
render() {
return (
<userContext.Provider value={this.state.user}>
<Main/>
</userContext.Provider>
);
}
}
export default App;
这是一个很好的开始,包装Main组件userContext.Provider确保您传递的值可以在任何子Main组件中使用。
申请 Context.Consumer
userContext.Consumer接收一个函数作为一个孩子。此函数接收当前上下文值(作为 prop 传递给 的值userContext.Provider)并返回一个 React 节点。
import Sidebar from './Sidebar';
import Content from './Content';
import Avatar from './Avatar';
import {userContext} from './userContext';
function Main(props) {
return (
<Sidebar/>
<userContext.Consumer>
{({value}) => {
<Avatar value={value}/>
}}
</userContext.Consumer>
<Content/>
)
}
export default Main;
在这种情况下,它接收App组件state.user作为值并呈现Avatar组件。
从嵌套组件更新上下文
到目前为止,您已经使用 React Context 将数据传递给需要它的组件,而无需手动传递 props。接下来,您将需要能够从嵌套的子组件更新上下文。
考虑一个用于注销用户的按钮。
在这种情况下,您可以通过相同的上下文向下传递函数,以允许使用者更新上下文。
import React from 'react';
import Main from './Main';
import {userContext} from './userContext';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {}
};
this.logout = this.logout.bind(this);
}
logout() {
this.setState({user: {}});
}
componentDidMount() {
// get and set currently logged in user to state
}
render() {
const value = {
user: this.state.user,
logoutUser: this.logout
}
return (
<userContext.Provider value={value}>
<Main/>
</userContext.Provider>
);
}
}
export default App;
传递给更新上下文的函数可以在组件内的任何嵌套组件中使用userContext.Provider。
import Sidebar from './Sidebar';
import Content from './Content';
import Avatar from './Avatar';
import LogoutButton from './LogoutButton';
import {userContext} from './userContext';
function Main(props) {
return (
<Sidebar/>
<userContext.Consumer>
{({user, logoutUser}) => {
return (
<Avatar user={user}/>
<LogoutButton onClick={logoutUser}/>
);
}}
</userContext.Consumer>
<Content/>
)
}
export default Main;
注销按钮已添加到Main组件中。
以上是一个如何使用 React 的 Context API 来管理用户状态的示例。
结论
在本文中,您了解了 React Context 和使用Context.Producerand Context.Consumer。
如果您想了解有关 React 的更多信息,请查看我们的How To Code in React.js系列,或查看我们的 React 主题页面以获取练习和编程项目。