#reactjs #typescript
#reactjs #typescript
Вопрос:
У меня возникли проблемы с доступом к состоянию компонента из обратного вызова.
Значение состояния 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.
Есть идеи?
редактировать # 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
and 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
существует, чтобы убедиться, что изменения правильно подобраны