【问题标题】:How to change the value of a Context with useContext?如何使用 useContext 更改 Context 的值?
【发布时间】:2019-07-11 07:54:41
【问题描述】:

在 React 16.8+ 中使用 useContext 钩子效果很好。您可以毫无问题地创建组件、使用钩子并利用上下文值。

我不确定的是如何将更改应用到 Context Provider 值。

1) useContext 钩子严格来说是使用上下文值的一种方式吗?

2) 有没有推荐的方法,使用 React 钩子来更新子组件的值,然后触发组件重新渲染使用 useContext 钩子与此上下文的任何组件?

const ThemeContext = React.createContext({
  style: 'light',
  visible: true
});

function Content() {
  const { style, visible } = React.useContext(ThemeContext);

  const handleClick = () => {
    // change the context values to
    // style: 'dark'
    // visible: false
  }

  return (
    <div>
      <p>
        The theme is <em>{style}</em> and state of visibility is 
        <em> {visible.toString()}</em>
      </p>
      <button onClick={handleClick}>Change Theme</button>
    </div>
  )
};

function App() {
  return <Content />
};

const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.2/umd/react-dom.production.min.js"></script>

【问题讨论】:

    标签: javascript reactjs react-hooks react-context


    【解决方案1】:

    Hooks FAQ 的How to avoid passing callbacks down? 部分讨论了如何使用钩子更新上下文。

    如果使用useContext 的组件在树的上方没有Provider,则传递给createContext 的参数将仅是默认值。相反,您可以创建一个 Provider 来提供 stylevisibility 以及切换它们的功能。

    示例

    const { createContext, useContext, useState } = React;
    
    const ThemeContext = createContext(null);
    
    function Content() {
      const { style, visible, toggleStyle, toggleVisible } = useContext(
        ThemeContext
      );
    
      return (
        <div>
          <p>
            The theme is <em>{style}</em> and state of visibility is
            <em> {visible.toString()}</em>
          </p>
          <button onClick={toggleStyle}>Change Theme</button>
          <button onClick={toggleVisible}>Change Visibility</button>
        </div>
      );
    }
    
    function App() {
      const [style, setStyle] = useState("light");
      const [visible, setVisible] = useState(true);
    
      function toggleStyle() {
        setStyle(style => (style === "light" ? "dark" : "light"));
      }
      function toggleVisible() {
        setVisible(visible => !visible);
      }
    
      return (
        <ThemeContext.Provider
          value={{ style, visible, toggleStyle, toggleVisible }}
        >
          <Content />
        </ThemeContext.Provider>
      );
    }
    
    ReactDOM.render(<App />, document.getElementById("root"));
    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    
    <div id="root"></div>

    【讨论】:

    • 所以说 useContext 钩子的唯一值是正确的,它允许您避免使用 Context.Consumer 父级包装组件并通过函数调用将上下文值传递给渲染的孩子?
    • @RandyBurgess 是的,没错。使用钩子创建上下文的工作方式与以前相同,只是您使用 useContext 钩子而不是带有您提到的渲染道具的 Context.Consumer 来使用它。
    • 不会将 value 设置为这样的对象,每次 Provider 重新渲染时都会重新渲染所有消费者,每个 the caveats section of the context documentation?
    • 那么,当使用useState 钩子处理提交给提供者的变量时,如何防止不断地重新渲染?
    • 您好,所以我们必须将状态函数作为 App.js 中的 props 传递给提供者。有没有办法将这些功能放在其他文件(服务或商店)中并导入它?这看起来很复杂,因为 useState 函数和调用它们的函数(例如 toggleVisible)必须在呈现上下文提供程序的组件中。我们不能从其他组件导入它。
    【解决方案2】:

    您可以使用这种方法,无论您有多少嵌套组件,它都可以正常工作。

    // Settings Context - src/context/Settings
    import React, { useState } from "react";
    
    const SettingsContext = React.createContext();
    
    const defaultSettings = {
      theme: "light",
    };
    
    export const SettingsProvider = ({ children, settings }) => {
      const [currentSettings, setCurrentSettings] = useState(
        settings || defaultSettings
      );
    
      const saveSettings = (values) => {
       setCurrentSettings(values)
      };
    
      return (
        <SettingsContext.Provider
          value={{ settings: currentSettings, saveSettings }}
        >
          {children}
        </SettingsContext.Provider>
      );
    };
    
    export const SettingsConsumer = SettingsContext.Consumer;
    
    export default SettingsContext;
    
    // Settings Hook - src/hooks/useSettings
    import { useContext } from "react";
    import SettingsContext from "src/context/SettingsContext";
    
    export default () => {
      const context = useContext(SettingsContext);
    
      return context;
    };
    
    // src/index
    ReactDOM.render(
      <SettingsProvider settings={settings}>
        <App />
      </SettingsProvider>,
      document.getElementById("root")
    );
    
    // Any component do you want to toggle the theme from
    // Example: src/components/Navbar
    const { settings, saveSettings } = useSettings();
    
    const handleToggleTheme = () => {
      saveSettings({ theme: "light" });
    };
    

    【讨论】:

    • “无效的钩子调用。只能在函数组件的主体内部调用钩子。”来自钩子
    • @ZiiM 而不是通过编辑这篇文章进行更改....您可以发布更改作为答案...我认为应该保留帖子的原始意图 :)
    • Boy FB 知道如何让事情变得复杂,TLDR,用 svelte 代替
    猜你喜欢
    • 1970-01-01
    • 2020-08-10
    • 1970-01-01
    • 2021-08-23
    • 1970-01-01
    • 2021-09-27
    • 2019-08-07
    • 1970-01-01
    • 2021-12-16
    相关资源
    最近更新 更多