我不相信仅靠高阶组件就可以解决您的支柱钻孔的基本问题。React Context更适合提供值和函数,通常是“想要在树的其他组件中使用根组件的功能”。
Context 提供了一种通过组件树传递数据的方法,而无需在每个级别手动传递 props。
在典型的 React 应用程序中,数据通过 props 自上而下(父级到子级)传递,但对于应用程序中许多组件所需的某些类型的 props(例如语言环境偏好、UI 主题)来说,这种使用可能很麻烦。Context 提供了一种在组件之间共享此类值的方法,而无需显式地通过树的每一层传递一个 prop。
首先创建您的 Context 和 Provider 组件:
const QnAContext = React.createContext({
answer: {
value: ""
},
doSomething: () => {}
});
const QnAProvider = ({ children }) => {
const answer = {
value: ""
};
const doSomething = () => {
console.log(answer.value);
console.log("Ready!");
};
return (
<QnAContext.Provider value={{ answer, doSomething }}>
{children}
</QnAContext.Provider>
);
};
在您的应用程序中渲染QnAProvider
您想要访问所提供值的 React 子树:
<QnAProvider>
<Page />
</QnAProvider>
使用上下文:
基于类的组件通过渲染道具模式使用上下文。
<QnAContext.Consumer>
{({ answer, doSomething }) => (
<SomeComponent doSomething={doSomething} answer={answer}>
We can?
</SomeComponent>
)}
</QnAContext.Consumer>
功能组件可以使用useContext React hook
const SomeComponent = ({ children }) => {
const { answer, doSomething } = useContext(QnAContext);
getAnswer = () => {
answer.value = "Some text";
doSomething();
};
return <div onClick={this.getAnswer}>{children}</div>;
};
这是使用高阶组件可能变得有用的地方。QnAContext.Consumer
您可以将渲染道具模式抽象为 HOC:
const withQnAContext = (Component) => (props) => (
<QnAContext.Consumer>
{(value) => <Component {...props} {...value} />}
</QnAContext.Consumer>
);
然后你可以装饰你想要注入上下文值的组件。
const DecoratedSomeComponent = withQnAContext(SomeComponent);
...
<DecoratedSomeComponent>We can with HOC?</DecoratedSomeComponent>
注意:做这一切的目的是将之前定义的值和函数移动Page
到上下文中,这样它们就不再从Page
通过Body
到SomeComponent
(或任何其他子组件)传递。
演示
沙盒代码:
const QnAContext = React.createContext({
answer: {
value: ""
},
doSomething: () => {}
});
const QnAProvider = ({ children }) => {
const answer = {
value: ""
};
const doSomething = () => {
console.log(answer.value);
console.log("Ready!");
};
return (
<QnAContext.Provider value={{ answer, doSomething }}>
{children}
</QnAContext.Provider>
);
};
const withQnAContext = (Component) => (props) => (
<QnAContext.Consumer>
{(value) => <Component {...props} {...value} />}
</QnAContext.Consumer>
);
class SomeComponent extends React.Component {
getAnswer = () => {
const { doSomething, answer } = this.props;
answer.value = "Some text";
doSomething();
};
render() {
return (
<button type="button" onClick={this.getAnswer}>
{this.props.children}
</button>
);
}
}
const DecoratedSomeComponent = withQnAContext(SomeComponent);
class Body extends React.Component {
render() {
return (
<div>
<div>
<QnAContext.Consumer>
{({ answer, doSomething }) => (
<SomeComponent doSomething={doSomething} answer={answer}>
We can?
</SomeComponent>
)}
</QnAContext.Consumer>
</div>
<div>
<DecoratedSomeComponent>We can with HOC?</DecoratedSomeComponent>
</div>
</div>
);
}
}
class Page extends React.Component {
render() {
return (
<div>
<div>
<Body />
</div>
</div>
);
}
}
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<QnAProvider>
<Page />
</QnAProvider>
</div>
);
}