问题描述
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>)
有时候我会得到:
1. value = 'x'
2. value = null
因此,基本上由于某种原因,尽管该值已更新为'x'
,但在嵌套函数中仍为null。
解决方法
首先,如果没有值,则需要为上下文提供一些默认值,然后将默认值设置为null。
export const FooContext = createContext(null);
通常,有两种方法可以在提供程序组件中传递值。您可以在提供程序组件中通过object
或tuple
到value
道具。
我将通过在提供程序组件中传递object
来举例。 @dna给出了一个tuple
的示例。
<FooContext.Provider value={{value,setValue}}>
{props.children}
</FooContext.Provider>
现在,如果要在另一个组件中使用该值,则需要像这样对对象进行解构
const {value,setValue} = useContext(FooContext);
如果您正确地调用了嵌套的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>)
}
<button onClick={SomeView.myFunction}>Click</myFunction>
输出:
1. value = 'x'
2. value = ' x'
现在,问题是为什么它返回一个字符值而不是状态值。
在Javascript中,字符串是字符数组。 例如
const string = ['s','t','r','i','n','g'];
//This is equivalent to
const string = "string";
在您的情况下,您的状态值可以是字符串。因此,当您解构字符串时,将获得字符串的第一个字符。
如果我给您一个例子,您将了解更多。
const string = "subrato";
const [str] = string;
console.log(str);
,
说明
这是经典的stale closure problem。因为您没有向我们展示如何使用library(tidyverse)
library(DT)
# custom function that uses CSS gradients to make the kind of bars I need
color_from_middle <- function (data,color1,color2)
{
max_val=max(abs(data))
JS(sprintf("isNaN(parseFloat(value)) || value < 0 ? 'linear-gradient(90deg,transparent,transparent ' + (50 + value/%s * 50) + '%%,%s ' + (50 + value/%s * 50) + '%%,%s 50%%,transparent 50%%)': 'linear-gradient(90deg,transparent 50%%,%s 50%%,transparent ' + (50 + value/%s * 50) + '%%)'",max_val,color2,max_val))
}
mtcars %>%
rownames_to_column() %>%
select(rowname,mpg) %>%
head(10) %>%
mutate(mpg = (mpg - 20) %>% round) %>%
datatable() %>%
formatStyle(
"mpg",background = color_from_middle(mtcars$mpg,'red','green')
)
,所以我无法确定闭包在哪里过时了,但是我敢肯定这就是原因。
您看到,在JS中,只要创建一个函数,它将在其闭包内部捕获周围的作用域,就可以将其视为创建状态时的状态的“快照” 。问题中的myFunction
是这些状态之一。
但是调用value
可能会在以后的某个时间发生,因为您可以传递myFunction
。假设您将其传递到某个地方的myFunction
。
现在,在1000毫秒超时之前,假设setTimeout(myFunction,1000)
已经重新渲染到<SomeView />
完成时,并且axios.get
已更新为value
。
此时,将创建'x'
的新版本,并在其结尾处捕获新值myFunction
。但是value = 'x'
已通过较旧版本的setTimeout
,该版本捕获了myFunction
。 1000毫秒后,将调用value = null
,并打印myFunction
。就是这样。
解决方案
像其他所有编程问题一样,正确处理陈旧的关闭问题的最佳方法是对根本原因有充分的了解。一旦意识到这一点,请谨慎编写代码,更改设计模式或进行其他操作。只是首先要避免该问题,不要让它发生!
这里讨论了这个问题,请参阅github上的#16956。在线程中,提出了多种模式和良好实践。
我不知道您的具体情况的详细信息,所以我无法告诉您解决问题的最佳方法。但是,非常幼稚的策略是使用对象属性而不是变量。
2. value = null
想法要依赖对象的稳定引用。
function SomeView() {
const [value] = useContext(FooContext);
const ref = useRef({}).current;
ref.value = value;
console.log('1. value =',ref.value);
}
return (<div>SomeView</div>)
}
创建相同对象ref = useRef({}).current
的稳定引用,该引用在重新渲染期间不会更改。然后将其放在ref
的闭包内。它就像门户一样,在封闭的边界上“传送”状态更新。
现在,即使仍然出现过时的关闭问题,有时您仍可以调用过时的myFunction
版本,这是无害的!由于旧的myFunction
与新的ref
相同,并且属性ref
保证是最新的,因为重新分配时总是将其ref.value
重新分配-呈现。
我认为提供者值中包含错误的部分,您可以这样做:
partitioner.class
,并且在...
<FooContext.Provider value={value}> // <-- 'value' is simply a value
{props.children}
</FooContext.Provider>
...
中,您可以通过以下方式来解构上下文值:
<SomeComponent />
但这是错误的,因为您将提供者值设置为简单值而不是元组。
解决方案,要使销毁工作正常进行,您应该像这样设置提供程序
const [value] = useContext(FooContext);
,
您的值状态之所以在函数内部提供值,而在嵌套函数内部提供空值,是因为这些原因。
-
第一个原因是您传递了一个箭头函数,这意味着它只会在onClick函数上返回一个值。
const myFunction = () => { console.log('2. value = ',value); }
-
第二个原因是您尚未调用所创建的嵌套函数
myFunction
。
而不是这样做,它将起作用:
-
在
SomeView()
内调用函数。myFunction();
-
更新您的功能。
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>;