Reactjs 反应:嵌套函数中未更新useContext值
我有一个简单的上下文,它设置了一些从后端获得的值,伪代码:Reactjs 反应:嵌套函数中未更新useContext值,reactjs,nested-function,use-context,Reactjs,Nested Function,Use Context,我有一个简单的上下文,它设置了一些从后端获得的值,伪代码: export const FooContext = createContext(); export function Foo(props) { const [value, setValue] = useState(null); useEffect(() => { axios.get('/api/get-value').then((res) => { const da
export const FooContext = createContext();
export function Foo(props) {
const [value, setValue] = useState(null);
useEffect(() => {
axios.get('/api/get-value').then((res) => {
const data = res.data;
setValue(data);
});
}, []);
return (
<FooContext.Provider value={[value]}>
{props.children}
</FooContext.Provider>
);
}
function App() {
return (
<div className="App">
<Foo>
<SomeView />
</Foo>
</div>
);
}
function SomeView() {
const [value] = useContext(FooContext);
console.log('1. value =', value);
const myFunction = () => {
console.log('2. value = ', value);
}
return (<div>SomeView</div>)
因此,基本上出于某种原因,该值在嵌套函数中保持为null,尽管已更新为
'x'
我认为提供程序值中存在错误的部分,请执行以下操作:
...
<FooContext.Provider value={value}> // <-- 'value' is simply a value
{props.children}
</FooContext.Provider>
...
但这是错误的,因为您将提供程序值设置为简单值而不是元组
解决方案要使您的分解工作正常,您应该如下设置提供程序
...
<FooContext.Provider value={[value]}> // <---- USE TUPLE NOTATION
{props.children}
</FooContext.Provider>
...
const {value, setValue} = useContext(FooContext);
。。。
//首先,如果没有值,则需要为上下文提供一些默认值,然后将默认值设置为null
export const FooContext = createContext(null);
通常,在提供者组件中传递值有两种方式。您可以将对象
或元组
传递给提供者组件中的值
道具
我将通过在Provider组件中传递一个对象来为您提供一个示例@dna给出了一个元组的例子
<FooContext.Provider value={{value,setValue}}>
{props.children}
</FooContext.Provider>
如果如下图所示正确调用了嵌套的myFunction()
,那么该值也将是x
,而不是null
function SomeView() {
const [value] = useContext(FooContext);
console.log('1. value =', value);
const myFunction = () => {
console.log('2. value = ', value);
}
SomeView.myFunction = myFunction; //updated line
return (<div>SomeView</div>)
}
现在,问题是它为什么返回单个字符值而不是状态值
在Javascript中,字符串是一个字符数组。
例如
在您的情况下,状态值可能是一个字符串。因此,当您解构字符串时,您将获得字符串的第一个字符
如果我给你举个例子,你会理解得更多
const string=“subrato”;
常量[str]=字符串;
console.log(str)代码>值状态在函数中返回值并在嵌套函数中返回null的原因是因为这些原因
第一个原因是您正在传递一个arrow函数,这意味着它将只返回onClick函数上的值
const myFunction = () => {
console.log('2. value = ', value);
}
function myFunction() {
console.log("2. value = ", value);
}
第二个原因是您没有调用您创建的嵌套函数myFunction
而不是这样做,它将起作用:
- 在
SomeView()中调用函数
- 更新你的函数
const myFunction = () => {
console.log('2. value = ', value);
}
function myFunction() {
console.log("2. value = ", value);
}
- 或者,如果希望它与旧代码一起运行,请传递一个onClick侦听器,它将启动myFunction(),并将console.log记录该值
const myFunction = () => {
console.log("2. value = ", value);
};
return <button onClick={myFunction}>Click me</button>;
constmyfunction=()=>{
console.log(“2.value=,value”);
};
返回点击我;
解释
这是如此经典。我不知道闭包在哪里过时,因为您没有向我们展示如何使用myFunction
,但我确信这就是原因
你知道,在JS中,每当你创建一个函数时,它将在它的闭包中捕获周围的范围,在它的创建点问题中的代码>值
是这些状态之一
但是调用myFunction
可能会在以后某个时候发生,因为您可以传递myFunction
。比方说,您将它传递给setTimeout(myFunction,1000)
某个地方
现在,在1000毫秒超时之前,假设
组件已经在axios.get
中重新呈现,并且值更新为'x'
此时创建了myFunction
的新版本,在该版本的结尾处捕获了新值value='x'
。但是setTimeout
传递的是myFunction
的较旧版本,它捕获value=null
。1000毫秒后,调用myFunction
,并打印2。value=null
。事情就是这样
解决方案
与所有其他编程问题一样,正确处理过时闭包问题的最佳方法是充分了解根本原因。一旦意识到了这一点,就要谨慎地编写代码,更改设计模式或其他任何东西。首先要避免这个问题,不要让它发生
这里讨论了这个问题,请参阅github上的。在线程中,建议了多种模式和良好实践
我不知道你具体案例的细节,所以我不知道什么是解决你问题的最佳方式。但一个非常简单的策略是使用对象属性而不是变量
function SomeView() {
const [value] = useContext(FooContext);
const ref = useRef({}).current;
ref.value = value;
console.log('1. value =', value);
const myFunction = () => {
console.log('2. value = ', ref.value);
}
return (<div>SomeView</div>)
}
函数SomeView(){
const[value]=useContext(FooContext);
const ref=useRef({}).current;
参考值=价值;
console.log('1.value=',value);
常量myFunction=()=>{
console.log('2.value=',ref.value);
}
返回(SomeView)
}
其思想是依赖于对象的稳定引用
ref=useRef({}).current
为同一对象创建一个稳定的引用ref
,该引用在重新渲染时不会改变。然后将其放在myFunction
的闭包中。它就像一个门户,“传送”状态更新越过闭包的边界
现在,即使陈旧的闭包问题仍然存在,有时您仍然可以调用过时版本的myFunction
,这是无害的!因为旧的ref
与新的ref
相同,并且它的属性ref.value
保证是最新的,因为在重新渲染时总是重新分配它ref.value=value
。在哪里调用myFunction
?它似乎在代码中,myFunction并没有被调用,对我来说就像一个过时的闭包,尽管我不能说,因为你的伪代码函数并没有在任何地方被调用,每次我点击一个按钮(不是在代码中)myFunction都会被调用。按钮是组件的一部分。添加了我的更新答案,其中说明了如何在父函数之外调用嵌套函数?@Bob SacamanoI很抱歉误导您。我将provider中的值设置为元组。我没有正确地编写伪代码。在实际情况中,它是一个元组。这个答案非常有用。是的,这确实是一个陈旧的问题:)
myFunction();
function myFunction() {
console.log("2. value = ", value);
}
const myFunction = () => {
console.log("2. value = ", value);
};
return <button onClick={myFunction}>Click me</button>;
function SomeView() {
const [value] = useContext(FooContext);
const ref = useRef({}).current;
ref.value = value;
console.log('1. value =', value);
const myFunction = () => {
console.log('2. value = ', ref.value);
}
return (<div>SomeView</div>)
}