我无法从回调访问组件状态。
状态num
的值正确更改,但是在加载时定义的回调函数看不到这种更改。
import React, {useState} from "react";
class MyObject {
callback: (() => void);
constructor(callback: () => void) {
this.callback = callback;
}
};
export const TestPage = () => {
const [num, setNum] = useState<number>(0);
// will print the changing values
console.log(`num: ${num}`);
const counter = useState<MyObject>(() => new MyObject(() => {
// will always print 0
console.log(`from callback. num: ${num}`);
}))[0];
const btnClick = () => {
setNum((new Date()).getTime());
counter.callback();
};
return (
<div>Test, num: {num}
<div>
<button onClick={btnClick}>click to update state</button>
</div>
</div>
);
}
以下纯非反应代码不会发生以上情况
let myVar = 0;
const obj = new MyObject(() => console.log(`myVar: ${myVar}`));
obj.callback(); // prints 0
myVar = 1;
obj.callback(); // prints 1
编辑:我试图用闭包来重现问题,但是我不能
const makeFun = (callback: (() => void)) => {
return () => {
callback();
}
}
let myVar = 0;
const f1 = makeFun(() => console.log(`myVar: ${myVar}`));
myVar = 1;
const f2 = makeFun(() => console.log(`myVar: ${myVar}`));
f1(); // prints 1
f2(); // prints 1
所以我怀疑是React和useState特有的。
有什么想法吗?
edit#2 :解决方案。这是使它在非反应代码中可见的方法。
let callbackPersistent: (() => void);
const createCallback = (callback: (() => void)) => {
if (!callbackPersistent) {
callbackPersistent = callback;
}
return callbackPersistent;
}
const myFunction = (num:number) => {
const callback = createCallback(() => console.log(`callback num: ${num}`));
callback();
};
然后将其命名为
myFunction(0); // prints 0
myFunction(1); // prints 0
只有一个回调实例,它指向首次创建后看到的值。
编辑#3 :
可以使用useRef
和useEffect
找到合理的解决方案,如下所示:
import React, {useEffect, useRef, useState} from "react";
class MyObject {
callback: (() => void);
constructor(callback: () => void) {
this.callback = callback;
}
};
export const TestPage = () => {
const [num, setNum] = useState<number>(0);
const numRef = useRef(num);
// will print the changing values
console.log(`num: ${num}`);
const counter = useState<MyObject>(() => new MyObject(() => {
// will always print the latest value
console.log(`from callback. num: ${numRef.current}`);
}))[0];
useEffect(()=> {numRef.current = num}, [num]);
const btnClick = () => {
setNum(new Date().getTime());
counter.callback();
};
return (
<div>Test, num: {num}
<div>
<button onClick={btnClick}>click to update state</button>
</div>
</div>
);
}
useRef
保证numRef
在多次调用中始终是同一对象。当num
的值更改时,onEffect
调用将更新numRef.current
的值,然后由回调进行访问。仅存在回调和numRef
的一个实例,以确保正确提取了更改
答案 0 :(得分:0)
回调函数在num
状态下有一个closure
,其值为零,因为在定义回调时,num
的值为零。
要解决此问题,您可以使用useEffect
钩子,该钩子使用新的回调函数创建一个新的MyObject
,该回调函数对最新值num
进行封闭。
useEffect(() => {
// update counter state
}, [num]);
非反应代码之所以能按预期工作的原因是因为对变量而不是其值关闭功能。因此,在第二个代码示例中,传递给MyObject
的构造函数的回调函数对myVar
变量进行了关闭。这意味着,如果您更改myVar
的值,则回调函数在被调用时将记录myVar
的最新值。
现在您可能会问,如果闭包是在变量而不是变量的值之上,那么React代码中的回调函数不应该看到num
的最新值吗?
造成回调函数总是记录为零的原因是因为在React中,回调函数看到定义了该回调函数时组件中可用的state
或prop
的值
我认为您根本不应该使用MyObject
类。如果您有一些复杂的状态,那么我建议您考虑使用useReducer
挂钩来管理状态。