【问题标题】:React.useContext not updatingReact.useContext 没有更新
【发布时间】:2021-12-25 18:52:02
【问题描述】:

这是我第一次在应用程序中使用 React 上下文挂钩,我正在尝试设置 SelectedBackgroundContext,但它不会更新。 console.log(typeof selectBackground) 确实显示为一个函数,所以我相信它正在正确导入,但不确定为什么它没有更新。

我的所有代码都可以在下面的这个 CodeSandbox 链接中找到。我遇到问题的那一行是 child.js:8

孩子

export default function Child() {
  const { selectedBackground } = useContext(SelectedBackgroundContext);
  const { selectBackground } = useContext(SelectedBackgroundContext);

  selectBackground(null); //Should render text saying "None" instead of image
  console.log(selectedBackground);

  const renderSelected = (context) => {
    if (context) {
      return (
        <img
          style={{ height: "200px" }}
          src={context}
          key={context + "Thumbnail"}
          alt={"thumbnail of " + context}
        />
      );
    } else {
      return <p>None</p>;
    }
  };

  return (
    <div>
      <p>Background:</p> {renderSelected(selectedBackground)}
    </div>
  );
}

上下文

export const SelectedBackgroundContext = React.createContext({
  selectedBackground:
    "https://lp-cms-production.imgix.net/2019-06/81377873%20.jpg?fit=crop&q=40&sharp=10&vib=20&auto=format&ixlib=react-8.6.4",
  selectBackground: () => {}
});

如果有任何建议,我将不胜感激!

【问题讨论】:

    标签: reactjs react-hooks react-context use-context


    【解决方案1】:

    问题

    您试图提供上下文值作为上下文值,这当然根本行不通。您实际上是在为您的应用程序提供默认上下文值,因为 App 在 ReactTree 中它上面没有 SelectedBackgroundContext 上下文提供程序。

    当您直接从函数伙伴更新上下文值时,您还在 Child 中编写了一个无意的副作用。

    selectBackground(null); // <-- unintentional side-effect
    console.log(selectedBackground); // <-- doesn't log updated state immediately
    

    解决方案

    App 需要“填写”selectedBackgroundselectBackground 回调值。 App 本身不能使用它提供的SelectedBackgroundContextApp 应该有一些本地组件状态来存储和更新 selectedBackground 值。

    function App() {
      const [selectedBackground, selectBackground] = useState(
        "https://lp-cms-production.imgix.net/2019-06/81377873%20.jpg?fit=crop&q=40&sharp=10&vib=20&auto=format&ixlib=react-8.6.4"
      );
    
      return (
        <SelectedBackgroundContext.Provider
          value={{ selectedBackground, selectBackground }}
        >
          <Child />
        </SelectedBackgroundContext.Provider>
      );
    }
    

    Child 中,使用useEffect 挂钩发出副作用并“监听”值的变化。

    import React, { useEffect, useContext } from "react";
    import { SelectedBackgroundContext } from "./context";
    
    export default function Child() {
      const { selectBackground, selectedBackground } = useContext(
        SelectedBackgroundContext
      );
    
      useEffect(() => {
        selectBackground(null);
      }, [selectBackground])
    
      useEffect(() => {
        console.log(selectedBackground);
      }, [selectedBackground])
    
      const renderSelected = (context) => {
        if (context) {
          return (
            <img
              style={{ height: "200px" }}
              src={context}
              key={context + "Thumbnail"}
              alt={"thumbnail of " + context}
            />
          );
        } else {
          return <p>None</p>;
        }
      };
    
      return (
        <div>
          <p>Background:</p> {renderSelected(selectedBackground)}
        </div>
      );
    }
    

    【讨论】:

      【解决方案2】:

      下面的链接显示了我首选的用于处理上下文的样板(您可以按下按钮来打开或关闭背景)。

      codesandbox link

      样板具有以下结构,遵循redux的理念。

      ├── package.json
      └── src
          ├── App.js
          ├── child.js
          ├── context
          │   ├── actions.js
          │   ├── reducer.js
          │   └── store.js
          └── index.js
      

      action.js

      export const actions = {
        SET_BACKGROUND: "SET_BACKGROUND"
      };
      
      

      它列出了上下文中所有允许的操作。

      reducer.js

      import { actions } from "./actions.js";
      
      export const reducer = (state, action) => {
        switch (action.type) {
          case actions.SET_BACKGROUND:
            return { ...state, background: action.background };
          default:
            return state;
        }
      };
      

      这是对上下文进行实际更改的地方。减速器首先检查动作类型,。然后根据动作类型,对上下文状态进行相应的修改。

      store.js

      import * as React from "react";
      import { reducer } from "./reducer.js";
      import { actions } from "./actions.js";
      
      import { originalBackground } from "../child";
      
      export const initialState = {
        background: originalBackground
      };
      
      export const AppContext = React.createContext();
      
      export const Provider = (props) => {
        const { children } = props;
        const [state, dispatch] = React.useReducer(reducer, initialState);
      
        const value = {
          background: state.background,
          setBackground: React.useCallback(
            (val) => dispatch({ type: actions.SET_BACKGROUND, background: val }),
            []
          )
        };
      
        return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
      };
      
      

      这是存储上下文状态的地方。上下文状态包含值(例如background)和触发修改这些值的函数(例如setBackground)。请注意,函数本身不会修改任何内容。它调度一个动作,该动作将被reducer捕获以进行实际的状态修改。

      App.js

      import React from "react";
      import { Provider } from "./context/store";
      import Child from "./child";
      
      function App() {
        return (
          <Provider>
            <Child />
          </Provider>
        );
      }
      
      export default App;
      

      Provider 包裹在访问上下文的组件上。如果上下文对整个应用程序是全局的,Provider 可以在 index.js 中环绕 App

      child.js

      import React, { useContext } from "react";
      import { AppContext } from "./context/store";
      
      export const originalBackground =
        "https://lp-cms-production.imgix.net/2019-06/81377873%20.jpg?fit=crop&q=40&sharp=10&vib=20&auto=format&ixlib=react-8.6.4";
      
      export default function Child() {
        const { background, setBackground } = useContext(AppContext);
      
        const renderSelected = (context) => {
          if (context) {
            return (
              <img
                style={{ height: "200px" }}
                src={context}
                key={context + "Thumbnail"}
                alt={"thumbnail of " + context}
              />
            );
          } else {
            return <p>None</p>;
          }
        };
      
        const toggleBackground = () => {
          if (background) {
            setBackground(null);
          } else {
            setBackground(originalBackground);
          }
        };
      
        return (
          <div>
            <button onClick={toggleBackground}>
              {background ? "Background OFF" : "Background ON"}
            </button>
            <p>Background:</p> {renderSelected(background)}
          </div>
        );
      }
      

      这显示了上下文是如何在组件中使用的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-12-25
        • 1970-01-01
        • 2020-09-01
        • 2011-03-09
        • 2012-03-24
        • 2018-07-25
        • 2021-12-05
        相关资源
        最近更新 更多