Как изменить значение контекста при использовании React Hook of useContext

С использованием useContext Хук с React 16.8+ работает хорошо. Вы можете создать компонент, использовать хук и использовать значения контекста без каких-либо проблем.

В чем я не уверен, так это в том, как применить изменения к значениям провайдера контекста.

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>

1 ответ

Решение

Как обновить контекст с помощью ловушек, обсуждается в разделе Как избежать передачи обратных вызовов? часть FAQ по Крюкам.

Аргумент передан createContext будет только значением по умолчанию, если компонент, который использует useContext не имеет Provider выше это далее вверх по дереву. Вы могли бы вместо этого создать Provider который поставляет style а также visibility а также функции для их переключения.

пример

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>

Вы можете использовать этот подход, независимо от того, сколько у вас вложенных компонентов, он будет работать нормально.

// 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"});
};
Другие вопросы по тегам