I am using React and have the following object:
const [records, setRecords] = useState(
[
{id: 1, correct: false},
{id: 2, correct: false},
{id: 3, correct: false},
{id: 4, correct: false},
{id: 5, correct: false}
]);
To update the object I have the following:
const onCorrectAnswerHandler = (id, correct) => {
setRecords(
records.map((record) => {
if (record.id === id) {
return { id: id, correct: true };
} else {
return record;
}
})
);
}
Here's the problem:
I want to run another function called isComplete
after it but within the Handler function, that uses the changed records
object, but it appears to use the original unchanged 'records' object (which console.log confirms).
e.g.
const onCorrectAnswerHandler = (id, correct) => {
setRecords(
records.map((record) => {
if (record.id === id) {
return { id: id, correct: true };
} else {
return record;
}
})
);
isComplete(records);
}
Console.log(records) confirms this. Why does it not use the updated records
since the isComplete
function runs after the update, and how can I get it to do this?
CodePudding user response:
Try renaming the function as React sees no change in the object and likewise when you are using an array or object in a state. Try to copy them out by storing them in a new variable.
setRecords(
const newRecords = records.map((record) => {
if (record.id === id) {
return { id: id, correct: true };
} else {
return record;
}
})
//seting this now triggers an update
setRecords(newRecords);
);
Then as per react documentation it's better to listen to changes with lifecycle methods and not setting state immediately after they are changed because useState
is asynchronous.
so use useEffect
to listen to the changes to set is Complete
useEffect(() => {
isComplete(records)
}, [records])
I hope this helps you?
CodePudding user response:
This is due to the fact that setState
is not actually synchronous. The stateful value is not updated immediately when setState
is called, but on the next render cycle. This is because React
does some behind the scenes stuff to optimise re-renders.
There are multiple approaches to get around this, one such approach is this:
If you need to listen to state
updates to run some logic you can use the useEffect
hook.
useEffect(() => {
isComplete(records)
}, [records])
This hook is pretty straightforward. The first argument is a function
. This function
will run each time if one of the variables in the dependency array updates. In this case it will run each time records
update.
CodePudding user response:
You can modify above function onCorrectAnswerHandler to hold the updated records in temporary variable and use to update state and call isComplete func
const onCorrectAnswerHandler = (id, correct) => {
let _records = records.map((record) => {
if (record.id === id) {
return {
id: id,
correct: true
};
} else {
return record;
}
})
setRecords(_records);
isComplete(_records);
}
CodePudding user response:
try this please.
const onCorrectAnswerHandler = (id) => {
records.forEach(r =>{
if(r.id == id) r.correct=true;
});
setRecords([...records]);
}