上次说到,我们其实可以把共享的东西封装成一个组件,这样可以让我们的代码结构更加清晰。怎么做呢?在之前创建的UserContext.js文件中,创建并导出了一个Context,用的React.createContext函数,现在我们不需要这个Context了,我们把这个Context直接解构成两个组件,Provider和Consumer。然后创建一个名为UserProvider的组件,在render函数中,我们return一个Provider组件,Provider组件的子组件为Provider组件所有的属性。代码如下:
const { Provider, Consumer } = React.createContext();
class UserProvider extends React.Component {
render() {
return (
<Provider>{this.props.children}</Provider>
)
}
}
然后把App组件中UserContext.Provider的属性值剪切到Provider组件中来。代码如下:
<Provider value={
{
user: this.state.user,
onLogin: this.loginHandler,
onLogout: this.logoutHandler
}
}>
把App组件中的共享变量也剪切到这里来。完整代码如下:
import React from 'react';
import { User } from './helper'
const { Provider, Consumer } = React.createContext();
class UserProvider extends React.Component {
state = {
user: User,
}
loginHandler = user => {
this.setState({ user: user })
}
logoutHandler = () => {
this.setState({ user: null })
}
render() {
return (
<Provider value={
{
user: this.state.user,
onLogin: this.loginHandler,
onLogout: this.logoutHandler
}
}>
{this.props.children}
</Provider>
)
}
}
export { UserProvider, Consumer as UserConsumer }
我们看到导出了两个对象,一个自定义的UserProvider组件,一个createContext解构来的Consumer组件,重命名为UserConsumer。
首先修改App组件,导入新创建的组件,把老的UserContext.Provider替换为UserProvider,由于App组件里已经没有了this.state.user,我们就要用UserConsumer组件来使用共享user变量。代码如下:
return (
<UserProvider>
<UserConsumer>
{
({ user }) =>
user
?
(<Main />)
:
(<Login />)
}
</UserConsumer>
</UserProvider>
)
类似的,修改Login组件,代码如下:
<UserConsumer>
{
({ onLogin }) => (
<div>
<form onSubmit={e => submitHandler(e, onLogin)}>
<label>
用户名
<input
name="username"
value={username}
onChange={inputChangeHandler}
/>
</label>
<label>
密码
<input
name="password"
type="password"
value={password}
onChange={inputChangeHandler}
/>
</label>
{error && <div>{error.message}</div>}
<button type="submit" disabled={loading}>登录</button>
</form>
</div>
)
}
</UserConsumer>
修改MessageList组件,代码如下:
<UserConsumer>
{
({ user }) => (
<div>
<div>
没有邮件, {user.name}
</div>
</div>
)
}
</UserConsumer>
修改Menu组件,代码如下:
<UserConsumer>
{
({ user, onLogout }) => (
<div>
<img
src={user.avatar}
onClick={this.toggleMenu}
ref={this.avatarRef}
/>
{this.state.visible && (
<ul>
<li onClick={onLogout}>退出登录</li>
</ul>
)}
</div>
)
}
</UserConsumer>
修改完毕,运行程序发现和之前没什么两样,但我们的代码结构变得更加清晰了。譬如App组件,已经没有了任何关于状态和回调函数的代码,完全可以写成函数形式的组件了。
下一回我们要讲一讲在同一个应用中使用多个Context Provider。