介绍
在 React 中,数据通过 props(父组件到子组件)从上到下流动是很常见的,但这并不总是理想的。在某些情况下,您的数据需要可用于不同深度的许多嵌套组件,并且传递道具变得难以维护且容易出错。
在这种情况下,使用Redux来管理状态似乎是个好主意,但许多人认为它不应该是您的首选。
React Context是跨组件共享数据的替代解决方案,而无需在每个级别手动传递 props。
在本文中,您将探索 Context API 并了解如何使用它来管理用户状态。
先决条件
要阅读本文,您需要:
本教程已通过 Node v15.3.0、npm
v6.14.9 和react
v17.0.1 验证。
了解问题
下面是一个Page
传递 auser
和avatarSize
prop的组件示例:
<Page user={user} avatarSize={avatarSize} />
它呈现一个PageLayout
组件:
<PageLayout user={user} avatarSize={avatarSize} />
它呈现一个NavigationBar
组件:
<NavigationBar user={user} avatarSize={avatarSize} />
渲染 aLink
并且Avatar
使用user
andavatarSize
道具:
<Link href={user.permalink}>
<Avatar user={user} size={avatarSize} />
</Link>
在上面的例子中,只有Avatar
组件实际使用了user
prop。然而,它的每个祖先组件(父、祖父等)都会接收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.Producer
and Context.Consumer
。
如果您想了解有关 React 的更多信息,请查看我们的How To Code in React.js系列,或查看我们的 React 主题页面以获取练习和编程项目。