В моем компоненте функции реагирования я отправляю запрос на несколько серверов и обновляю значение хука состояния через асинхронные обещания, добавляя результаты сервера к последнему значению, но как только обещание создано, значение хука состояния не обновляется в текущем обещании, поэтому, если другое обещание обновляет значение состояния, другие запущенные обещания не получают информацию, и в результате они используют более старую версию состояния для дальнейших обновлений состояния.
приведенный ниже код представляет собой упрощенную версию моего компонента, я ожидаю увидеть одно и то же (и наиболее обновленное) значение из журнала консоли в строке 19 для каждого обещания, пока выполняется несколько обещаний, и состояние обновляется.
function App() {
const [myArray, setMyArray] = useState([0,1,2])
const sleep = (ms:number) => {
return new Promise(resolve => setTimeout(resolve, ms));
}
const updateArray = () => {
setMyArray([...myArray, myArray.length])
}
const firePromise = () => {
new Promise(async (resolve) => {
const timeStamp = new Date().getTime()
let repeatTime = 0
while(repeatTime < 12){
console.info("array: ", myArray, "promiseIdenifier: ", timeStamp);
repeatTime += 1
await sleep(1000)
}
resolve({timeStamp, myArray})
}).then(val => {
console.info("resolved: ", val);
}).catch(err => {
console.info("rejected: ", err);
})
}
return (
<div className = "App">
<button onClick = {firePromise}>new promise</button>
<button onClick = {updateArray}>updateArray</button>
</div>
);
}
export default App;
Отличный намек, ты точно спас мне целый день, бро =) @Konstantin
после рендеринга компонента React можно предположить, что текущее «состояние» компонента существует как снимок.
«myArray» в console.info — это «myArray» на момент создания firePromise. так что правильно оставить первое значение. (Каждый раз, когда компонент визуализируется, создается новый firePromise.)
Есть выход. Первый — использовать ref, а второй — использовать setState.
Первый
function App() {
const myArray = useRef<Array<number>>([0, 1, 2]);
const sleep = (ms: number) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
const updateArray = () => {
myArray.current.push(myArray.current.length);
};
const firePromise = () => {
new Promise(async (resolve) => {
const timeStamp = new Date().getTime();
let repeatTime = 0;
while (repeatTime < 12) {
console.info(
"array: ",
myArray.current,
"promiseIdenifier: ",
timeStamp
);
repeatTime += 1;
await sleep(1000);
}
resolve({ timeStamp, myArray: myArray.current });
})
.then((val) => {
console.info("resolved: ", val);
})
.catch((err) => {
console.info("rejected: ", err);
});
};
return (
<div className = "App">
<button onClick = {firePromise}>new promise</button>
<button onClick = {updateArray}>updateArray</button>
</div>
);
}
export default App;
Второй
function App() {
const [myArray, setMyArray] = useState([0, 1, 2]);
const sleep = (ms: number) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
const updateArray = () => {
setMyArray([...myArray, myArray.length]);
};
const firePromise = () => {
new Promise(async (resolve) => {
const timeStamp = new Date().getTime();
let repeatTime = 0;
while (repeatTime < 12) {
setMyArray((prevMyArray) => {
console.info("array: ", prevMyArray, "promiseIdenifier: ", timeStamp);
return prevMyArray;
});
repeatTime += 1;
await sleep(1000);
}
setMyArray((prevMyArray) => {
resolve({ timeStamp, prevMyArray });
return prevMyArray;
});
})
.then((val) => {
console.info("resolved: ", val);
})
.catch((err) => {
console.info("rejected: ", err);
});
};
return (
<div className = "App">
<button onClick = {firePromise}>new promise</button>
<button onClick = {updateArray}>updateArray</button>
</div>
);
}
export default App;
При передаче обратного вызова в функцию setState текущее состояние передается в качестве первого аргумента. это ярлык, использующий this.
В качестве состояния рекомендуется использовать значение, которое представление должно изменить при изменении значения. Изменение «myArray» не влияет на представление, поэтому использование ref — правильный метод.
прочитайте это: https://iqkui.com/a-complete-guide-to-useeffect/
Я использовал решение useRef
, и оно отлично сработало.
В этом случае вам лучше использовать refs, чем state, попробуйте с
useRef