【问题标题】:Is there a way to force multiple Context Consumers to share state?有没有办法强制多个上下文消费者共享状态?
【发布时间】:2019-07-23 14:46:02
【问题描述】:

我使用 React Context API 的主要目的是避免道具钻孔。现在我的 Context 包括一个 useState 和各种更新状态的函数 - 这些被放入一个 const 对象中,该对象作为 ActionsContext.Providervalue 属性传递。这是我当前组件层次结构的抽象:

Header
---NavPanel
ContentContainer
---Content (Context.Consumer being returned in this component)

其中 Header 和 ContentContainer 是同级元素,而 NavPanel 和 ContentContainer 是它们各自的子元素。

我最初将 Context.Consumer 放在 Content 中,因为其他元素不需要它。但是,我现在正在构建一个功能,其中 NavPanel 需要了解由 Context 管理的状态。于是我在NavPanel中放了另一个Consumer,才发现一个单独的Consumer就意味着一个单独的状态实例。

是否有任何聪明的解决方法可以让 NavPanel 和 Content 访问相同的状态,而不涉及将 Consumer 放在 Header 和 Content 的父组件中?这将导致我的应用程序当前的结构方式进行大量的道具钻探。

多个实例的 Codesandbox 示例:https://codesandbox.io/s/context-multiple-consumers-v2wte

【问题讨论】:

  • 取决于功能,尝试添加一个示例,说明您尝试做什么以及最佳方法是什么
  • 我认为将上下文向上移动更有意义。使用上下文的一件事是它有助于避免螺旋钻。我使用了一个自定义钩子来轻松地在两个需要它的组件中使用上下文:codesandbox.io/s/context-multiple-consumers-d67cg

标签: reactjs react-hooks react-context


【解决方案1】:

几件事:

  • 对于要共享的每个州,您应该只有一个提供者。
      <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);

代码沙盒https://codesandbox.io/s/context-multiple-consumers-vb9oj?fontsize=14

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-14
    • 1970-01-01
    相关资源
    最近更新 更多