React.useContext 没有更新

Posted

技术标签:

【中文标题】React.useContext 没有更新【英文标题】:React.useContext not updating 【发布时间】: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: () => 
);

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

【问题讨论】:

【参考方案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>
  );

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

【讨论】:

以上是关于React.useContext 没有更新的主要内容,如果未能解决你的问题,请参考以下文章

React useContext,useRedux子组件不更新

使用React useContext hook进行分派调用后,反应状态不会更新

React useContext 值没有从提供者传递过来?

React useContext 不会将值传递给深层嵌套的子级

React.useContext() 不断返回未定义?

React:useContext,如何从外部组件中检索它?