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
需要“填写”selectedBackground
和 selectBackground
回调值。 App
本身不能使用它提供的SelectedBackgroundContext
。 App
应该有一些本地组件状态来存储和更新 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进行分派调用后,反应状态不会更新