我正在使用React Context API,其主要目的是避免道具钻探。现在,我的Context包含一个useState
和各种更新状态的函数-这些函数被放入const对象中,该对象作为value
的{{1}}属性传递。这是我当前组件层次结构的抽象:
ActionsContext.Provider
其中Header和ContentContainer是同级元素,NavPanel和ContentContainer是它们各自的子级。
我最初将Header
---NavPanel
ContentContainer
---Content (Context.Consumer being returned in this component)
放在Content中,因为其他元素不需要它。但是,我现在正在构建一个功能,NavPanel需要了解Context管理的状态。因此,我在NavPanel中放置了另一个Consumer,却发现单独的Consumer意味着状态的单独实例。
是否有任何明智的解决方法使NavPanel和Content访问权限处于同一状态,而不涉及将Consumer置于Header and Content的父组件中?这样一来,我的应用当前的结构方式就会导致大量的道具钻探。
多个实例的Codesandbox示例:https://codesandbox.io/s/context-multiple-consumers-v2wte
答案 0 :(得分:0)
几件事:
<ContextProvider>
<PartOne />
<hr />
<PartTwo />
</ContextProvider>
最好将上下文拆分为多个上下文,以便您传递值而不是对象。这样,当您更新状态时,React会检测到状态不同,而不是比较同一对象。
您的输入应为受控组件https://reactjs.org/docs/forms.html
如果您使用React 16.8而不是ContextConsumer,请考虑使用useContext API以获得更好的人体工程学。
进行这些更改后,您的代码将为:
MyContext.js
import React, { useState } from "react";
export const MyItemContext = React.createContext();
export const MySetItemContext = React.createContext();
export const MyHandleKeyContext = React.createContext();
const ContextProvider = props => {
const [itemBeingEdited, setItemBeingEdited] = useState("");
const handleKey = event => {
if (event.key === "Enter") {
setItemBeingEdited("skittles");
} else if (event.key === "K") {
setItemBeingEdited("kilimanjaro");
} else {
setItemBeingEdited("");
}
};
const editFunctions = {
itemBeingEdited,
setItemBeingEdited,
handleKey
};
return (
<MyItemContext.Provider value={itemBeingEdited}>
<MyHandleKeyContext.Provider value={handleKey}>
<MySetItemContext.Provider value={setItemBeingEdited}>
{props.children}
</MySetItemContext.Provider>
</MyHandleKeyContext.Provider>
</MyItemContext.Provider>
);
};
export default ContextProvider;
PartOne.js
import React, { useContext } from "react";
import ContextProvider, {
MyContext,
MyItemContext,
MySetItemContext,
MyHandleKeyContext
} from "./MyContext";
const PartOne = () => {
// blah
const itemBeingEdited = useContext(MyItemContext);
const handleKey = useContext(MyHandleKeyContext);
const setItem = useContext(MySetItemContext);
return (
<React.Fragment>
<span>{itemBeingEdited}</span>
<input
placeholder="Type in me"
onKeyDown={handleKey}
value={itemBeingEdited}
onChange={e => setItem(e.target.value)}
/>
</React.Fragment>
);
};
export default PartOne;
PartTwo.js
import React, { useContext } from "react";
import ContextProvider, {
MyContext,
MyItemContext,
MySetItemContext,
MyHandleKeyContext
} from "./MyContext";
const PartTwo = () => {
// blah
const itemBeingEdited = useContext(MyItemContext);
const handleKey = useContext(MyHandleKeyContext);
const setItem = useContext(MySetItemContext);
return (
<React.Fragment>
<span>{itemBeingEdited}</span>
<input
value={itemBeingEdited}
type="text"
placeholder="Type in me"
onChange={e => setItem(e.target.value)}
onKeyDown={handleKey}
/>
</React.Fragment>
);
};
export default PartTwo;
index.js
import React from "react";
import ReactDOM from "react-dom";
import PartOne from "./PartOne";
import PartTwo from "./PartTwo";
import ContextProvider from "./MyContext";
import "./styles.css";
function App() {
return (
<div className="App">
<ContextProvider>
<PartOne />
<hr />
<PartTwo />
</ContextProvider>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
CodeSandbox :https://codesandbox.io/s/context-multiple-consumers-vb9oj?fontsize=14