Context 的目的是对于全局的需要多处使用的数据,不希望通过 prop 一层层传递下去,而是可以直接使用。
childContextTypes & getChildContext
该方案在 16.3 后不再是推荐方案。
React 最初提供的 context 方案是提供 context 数据的组件通过静态的 childContextType
来定义 context 中各字段的数据类型,通过 getChildContext
成员方法来提供实际的 context 数据,其中,返回的数据可以搭配 prop 和 state 来提供响应式的数据。而消费 context 数据的组件通过静态的 contextTypes
来指定需要消费的 context 字段的类型集合,然后即可通过 this.context[fieldName]
来使用。
注意,消费 context 的组件只需要定义需要的字段的类型即可,不需要是全部。同时,也不需要是提供 context 的组件的直接子组件。
import PropTypes from 'prop-types';
class Button extends React.Component {
static contextTypes = {
color: PropTypes.string
};
render() {
return (
<button style={{background: this.context.color}}>
{this.props.children}
</button>
);
}
}
class Message extends React.Component {
render() {
return (
<div>
{this.props.text} <Button>Delete</Button>
</div>
);
}
}
class MessageList extends React.Component {
static childContextTypes = {
color: PropTypes.string
};
getChildContext() {
return {color: "purple"};
}
render() {
const children = this.props.messages.map((message) =>
<Message text={message.text} />
);
return <div>{children}</div>;
}
}
Context Provider & Consumer
React 16.3 后提出新的 context 方案 React.createContext(anyInitValue)
,然后使用其提供的 Provider
和 Consumer
来使用。
export const themes = {
light: {
foreground: '#000000',
background: '#eeeeee',
},
dark: {
foreground: '#ffffff',
background: '#222222',
},
};
export const ThemeContext = React.createContext({
theme: themes.dark,
toggleTheme: () => {},
});
export function ThemeTogglerButton() {
// The Theme Toggler Button receives not only the theme
// but also a toggleTheme function from the context
return (
<ThemeContext.Consumer>
{({theme, toggleTheme}) => (
<button
onClick={toggleTheme}
style={{backgroundColor: theme.background}}>
Toggle Theme
</button>
)}
</ThemeContext.Consumer>
);
}
function Content() {
return (
<div>
<ThemeTogglerButton />
</div>
);
}
class App extends React.Component {
constructor(props) {
super(props);
this.toggleTheme = () => {
this.setState(state => ({
theme:
state.theme === themes.dark
? themes.light
: themes.dark,
}));
};
// State also contains the updater function so it will
// be passed down into the context provider
this.state = {
theme: themes.light,
toggleTheme: this.toggleTheme,
};
}
render() {
// The entire state is passed to the provider
return (
<ThemeContext.Provider value={this.state}>
<Content />
</ThemeContext.Provider>
);
}
}
这种方案可以同时使用多个 context(provider 和 consumer 都可以作为普通标签来嵌套使用),并且可以使用 react-adopt 之类的库来解决 consumer 过多嵌套的问题。
// Theme context, default to light theme
const ThemeContext = React.createContext('light');
// Signed-in user context
const UserContext = React.createContext({
name: 'Guest'
});
class App extends React.Component {
state = {
signedInUser: {
name: 'Joe'
},
theme: 'dark'
};
render() {
const { signedInUser, theme } = this.state;
return (
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={signedInUser}>
<Content />
<hr />
<Content2 />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
}
function Content() {
return (
<ThemeContext.Consumer>
{theme => (
<UserContext.Consumer>
{user => (
<div>
<div>user: {user.name}</div>
<div>theme: {theme}</div>
</div>
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
// 组合来简化 consumer 的嵌套
const ComposeConsumer = adopt({
theme: <ThemeContext.Consumer />,
user: <UserContext.Consumer />
});
function Content2() {
return (
<ComposeConsumer>
{({ theme, user }) => (
<div>
<div>user: {user.name}</div>
<div>theme: {theme}</div>
</div>
)}
</ComposeConsumer>
);
}
contextType
Provider / Consumer 形式的 context,在 class 组件中使用时,初期只能在 render 系列的方法中才能使用。在 16.6.0 时添加了 contextType
来方便 class 组件在全生命周期内使用。
具体来说是通过静态的 contextType 指向创建成的 context,然后内部可以使用 this.context
来直接消费 context。
需要注意的是,这种形式只能消费一个 context,别的 context 还是需要通过 consumer 的形式来消费,并且 this.context 是 provider 提供的值。
const ThemeContext = React.createContext('light');
class App extends React.Component {
state = {
theme: 'dark'
};
render() {
const { theme } = this.state;
return (
<ThemeContext.Provider value={theme}>
<SingleCtx />
</ThemeContext.Provider>
);
}
}
class SingleCtx extends React.Component {
static contextType = ThemeContext;
render() {
return (
<div>
{this.context}
</div>
);
}
}
Context Hook
useContext(createdContext)
是将在 16.7 引入的 context hook,便于在无状态组件中消费 createContext
创建的 context。使用语法上比 Consumer 的简单很多,能直接使用普通的语法而不需要使用 render prop 的语法。
function App() {
const theme = useContext(ThemeContext)
const language = useContext(LanguageContext)
return <div>{theme} and {language}</div>
}