Почему стейт обновляется только после второго нажатия?
Подскажите пожалуйста, почему когда запускаю функцию на обновление стейта, то он обновляется только после второго нажатия?
const [isFormsValid, setIsFormsValid] = useState({})
const handleSelfValidation = (isFormValid: boolean, formIndex: number) => {
console.log(isFormValid, formIndex, 'test')
setIsFormsValid((prev) => ({
...prev,
[formIndex]: isFormValid,
}))
console.log(isFormsValid) // показывает впервый раз пустой массив, хотя должен показать обновленный, ведь перед консоль логом функция (setIsFormsValid) которая меняет состояние
};
//валидация происходит вот тут
const handleValid = () => {
members
.filter((member: Tourist) => {
return member.createdIn === touristCreatedIn && !member.isEmployee;
})
.forEach((member: any, index: any) => {
personSchema
.validate(member, { abortEarly: false })
.then(() => {
setFieldError({
[index]: {}
})
})
.catch((errs: any) => {
setFieldError({})
errs?.inner?.forEach((err: any) => {
setFieldError((prev)=> ({
...prev,
[index]: {
...prev[index],
[err.path]: err.message,
},
}))
});
});
personSchema
.isValid(member)
.then((v: any) => {
console.log('тут', v, index)
handleSelfValidation(v, index); // запуск хендлера
})
.catch((err: any) => {
// eslint-disable-next-line
console.error('TouristData YUP isValid Err', err);
});
});
// setTimeout(handleInfoCheck);
};
const handleInfoCheck = () => {
setFirstVisit();
if (
Object.values(isFormsValid).every((item: any) => {
return item === true;
})
) {
switch (permissionType) {
case 'tour':
history.push(`${addTourUrl}/tour-data`);
break;
case PERMISSION_TYPE_TRANZIT:
history.push(`${addTourUrl}/tranzit-data`);
break;
default:
history.push(`${addTourUrl}/tour-data`);
break;
}
}
};
Ответы (1 шт):
Автор решения: Konstantin Modin
→ Ссылка
setIsFormsValid это асинхронная функция, в то время как console.log() это синхронный код. Соответсвенно console.log() выполняется раньше чем изменится состояние. Поэтому в консоли будет отображено старое состояние. Вот пример:
// import React from "react";
const App = () => {
const [state, setState] = React.useState("very old");
const buttonHandler = () => {
setState("brand new");
console.log(`state is`, state);
};
return <button onClick={buttonHandler}>Click me</button>;
};
// export default App;
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Для корректного отображения state после его изменения можно воспользоваться хуком useEffect, добавив в массив зависимостей необходимый state который нужно отслеживать:
// import React from "react";
const App = () => {
const [state, setState] = React.useState("very old");
React.useEffect(() => {
console.log(`state is `, state);
}, [state]);
const buttonHandler = () => {
setState("brand new");
};
return <button onClick={buttonHandler}>Click me</button>;
};
// export default App;
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>