有没有一种方法可以迫使多个上下文使用者共享状态?

时间:2019-07-23 14:46:02

标签: reactjs react-hooks react-context

我正在使用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

1 个答案:

答案 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