React 16.3最终使Context API变得稳定。 从那时起,许多开发人员开始使用它来解决“ prop-drilling”问题-这个问题是您需要沿着不使用它的组件沿组件树向下传递一个prop
,以便访问被深渲染的组件中的prop
树倒 。 React文档指出了Context API的意图,如下所示:
在典型的React应用程序中, 数据是通过props自上而下(父级到子级)传递的,但是对于应用程序中许多组件所需的某些类型的props(例如,语言环境首选项,UI主题)而言,这可能会很麻烦。 上下文提供了一种在组件之间共享类似值的方法,而不必在树的每个级别上显式传递道具。
上下文旨在共享可被视为 React组件树(例如当前经过身份验证的用户,主题或首选语言)的“全局”数据 。
让我们看一下跟踪用户活动的示例。 为此,我们希望每次用户单击UI元素时都调用某个track
函数。
普通React中的Tracker
const track = event => console.log(`${event} occured`);
class App extends React.Component {
render() {
return <Toolbar track={track} />;
}
}
function Toolbar(props) {
return (
<div>
<TrackedButton track={props.track} />
</div>
);
}
function TrackedButton(props) {
const onClick = () => {
props.track("button click");
// do something on click
};
return <Button onClick={onClick} />;
}
我们需要在应用程序的许多不同组件中使用track
功能,并且使用没有任何高级模式的React,即使App
和Toolbar
没有使用它,也需要在整个组件树中传递track
。
上下文跟踪器
我们可以使用新的React Context API将track
函数注入所需的组件中:
const track = event => console.log(`${event} occured`);
const TrackerContext = React.createContext(track);
class App extends React.Component {
render() {
return (
<TrackerContext.Provider value={track}>
<Toolbar />
</TrackerContext.Provider>
);
}
}
function Toolbar(props) {
return (
<div>
{/* does not need to forward props anymore */}
<TrackedButton />
</div>
);
}
function TrackedButton(props) {
return (
<TrackerContext.Consumer>
{value => (
<Button
onClick={() => {
// call track
value("button click");
// do something on click
}}
/>
)}
</TrackerContext.Consumer>
);
}
我们创建一个TrackerContext
,用Provider
包裹根组件,这使track
函数可作为TrackedButton
组件中的value
使用。
但是,这不是我们在Context
API之前无法做到的,您始终可以在React中使用HOC (高阶组件) 来做到这一点 。 使用HOC,该代码甚至更具可读性。
跟踪器作为HOC
我们可以创建一个HOC,一个接受组件并返回一个新组件的函数,该组件将对原始组件进行一些增强。 在我们的例子中,我们将track
功能作为prop
注入到内部组件中。
const track = event => console.log(`${event} occured`);
const withTracker = track => Component => (props) => (
<Component track={track} {...props}/>
);
class App extends React.Component {
render() {
return (
/* no need to wrap root Component */
<Toolbar />
);
}
}
function Toolbar(props) {
return (
<div>
{/* need to use the HOC here */}
<TrackedButtonWrapped />
</div>
);
}
// TrackedButton is the same as in first solution
function TrackedButton(props) {
const onClick = () => {
props.track("button click");
// do something on click
};
return <Button onClick={onClick} />;
}
// Need to create a higher-order component out of TrackedButton
const TrackedButtonWrapped = withTracker(track)(TrackedButton)
这样做的好处是代码比使用Context
更干净:
-
App
不再需要ContextProvider
包装器。 -
TrackedButton
与纯React解决方案中的组件完全相同。 检索track
功能不再是它的责任。 这是因为我们将prop
检索转移到了TrackedButtonWrapped
HOC中 ,该HOC代替了我们在Toolbar
呈现。
那么React.createContext没用吗?
让我们回到React文档中给出的示例。 在那里, Context
用于主题应用程序。
上下文主题
该代码类似于“ 上下文跟踪”示例。 ThemeContext.Provider
提供theme
和切换 theme
的功能,该功能在根组件中设置state
。 这些变量仅在需要的地方使用-在ThemedButton
为按钮设置样式并在单击按钮时切换主题。
const ThemeContext = React.createContext();
class App extends React.Component {
state = {
theme: 'light'
}
toggleTheme = () => {
this.setState(({ theme }) => ({
theme: theme === 'light' ? 'dark' : 'light',
}));
}
render() {
const value = {
theme: this.state.theme,
toggleTheme: this.toggleTheme,
}
return (
<ThemeContext.Provider value={value}>
<Toolbar />
</ThemeContext.Provider>
);
}
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton(props) {
return (
<ThemeContext.Consumer>
{({ theme, toggleTheme }) => <Button theme={theme} onClick={toggleTheme}/>}
</ThemeContext.Consumer>
);
}
主题为HOC:首先尝试
让我们尝试应用与Tracking
Theming
相同的HOC模式。
剧透:这行不通。
const withTheme = InnerComponent => class extends React.Component {
state = {
theme: 'light'
}
toggleTheme = () => {
this.setState(({ theme }) => ({
theme: theme === 'light' ? 'dark' : 'light',
}));
}
render() {
return (
<InnerComponent theme={this.state.theme} toggleTheme={this.toggleTheme} />
)
}
}
class App extends React.Component {
render() {
return (
// again no Provider needed
<Toolbar />
);
}
}
function Toolbar(props) {
return (
<div>
{/* use the HOC here */}
<ThemedButtonWrapped />
{/* let's add another Button here */}
<ThemedButtonWrapped />
</div>
);
}
function ThemedButton(props) {
return <Button onClick={props.toggleTheme} theme={props.theme} />;
}
// Need to create a higher-order component out of ThemedButton
const ThemedButtonWrapped = withTheme(ThemedButton)
怎么了 如果我们添加另一个ThemedButton
,您会注意到它在这里不起作用。 每次创建HOC时,组件实例都以全新state
开始,因此按钮的主题彼此独立。 你可以在这里玩。
为什么在跟踪示例中有效? 在跟踪示例中,数据( track
功能)是静态的,并且从未更改,因此,使用相同功能的每个Button实例在这里都不是问题。
这是Context API
相对于HOC pattern
的最大优势,以及它如此强大的原因: 使用 Context
,数据在所有 Consumers
之间共享 。
摘要
这又是React文档对Context
用例的Context
:
上下文旨在共享可被视为React组件树的“全局”数据
我还要进一步说Context API
是用于由多个组件实例使用的全局动态数据的。 对于静态数据,您可能不需要Context
。 总是可以用注入props
的更简单的HOC代替它。
这是如何确定是否使用React的Context
API的一般指南:
最初发布于cmichel.io
From: https://hackernoon.com/you-might-not-need-react-context-e1adb35b2e04