Reputation: 31
I want to:
uploadImage
function below runs onDrop of file into Dropzone.)I am trying to confirm that I have the image at step 4, but I cannot confirm that via my console messages. I'm somehow stuck with the original state values despite log messages showing the state has already been updated.
I've tried adding extra asyncs to everything, and I've wrapped stuff in promises. I can confirm the readImage
function is fine. I can confirm the run sequence is readImage
, saveImage
, setGraphic
within saveImage, and then the console.log in uploadImage
which should have the updated values but doesn't.
const [graphic, setGraphic] = useState({ ...nullGraphic });
const readImage = async (img) => {
const reader = new FileReader();
return new Promise((resolve, reject) => {
reader.onerror = () => reader.abort();
reader.onabort = () => reject(new DOMException('Problem parsing input file.'));
reader.onload = () => resolve(reader.result);
reader.readAsDataURL(img);
});
};
const saveImage = async (img) => {
const imageRead = await readImage(img);
console.log(imageRead);
await setGraphic({
...graphic,
image: imageRead,
imageName: img.name,
imageType: img.type,
});
};
const uploadImage = async (img) => {
await saveImage(img);
console.log(graphic);
});
render() {
console.log(graphic);
return( <some dom> )
}
this is my strange console sequence:
Form.js:112 starting saveImage
Form.js:95 (snipped value of imageRead)
Form.js:237 {snipped updated values of graphic}
Form.js:114 done with saveImage
Form.js:116 {snipped original values of graphic}
My Theory
The useState hook is doing something funky.
The process copies the value when it starts instead of reading it whenever the console.log is being read, so the uploadImage
function gets the original values of the useState hook for the lifetime of that process.
Upvotes: 3
Views: 5557
Reputation: 138527
It might become clear why this would not work if we would simplify all those React magic into :
function withState(initial, fn) {
function setState(updated) {
setTimeout(() => fn(updated, setState), 0);
//...
}
setState(initial);
}
Now we can illustrate what happens:
withState(0, (counter, setCount) => {
console.log("before", counter);
if(counter < 10) setCount(counter + 1);
console.log("after", counter);
});
As you can see, setCount
will not change the state (count
) directly. It will actually not change the local count
variable at all. Therefore "before" and "after" will always log the same (as long as you don't mutate counter
directly).
Instead calling setCount
will be deffered (and batched in React), and then it will recall the whole component function passing in the new count.
Therefore it makes no sense trying to await
the setState call, also it makes no sense to log the state before and after it (as it won't change inbetween). Log it once in a component, if you set the state the whole component executes again and the logging will log the new state.
Upvotes: 1