简体   繁体   English

尽管它被更新,但React useState钩子返回旧的状态值

[英]React useState hook returns old state value despite it being updated

I want to: 我想要:

  1. upload an image into the browser (I do this with React Dropzone. My uploadImage function below runs onDrop of file into Dropzone.) 将图像上传到浏览器中(我使用React Dropzone执行此操作。下面的uploadImage函数将onDrop of file运行到Dropzone。)
  2. read the image as an actual image using the FileReader API. 使用FileReader API将图像作为实际图像读取。
  3. save the image file as well as image metadata into state, using React Hooks. 使用React Hooks将图像文件以及图像元数据保存到状态。
  4. make an API call that pushes this image to a server. 进行API调用,将此图像推送到服务器。

I am trying to confirm that I have the image at step 4, but I cannot confirm that via my console messages. 我试图确认我在步骤4中有图像,但我无法通过我的控制台消息确认。 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. 我已经尝试过为所有东西添加额外的asyncs,并且我已经在promises中包装了东西。 I can confirm the readImage function is fine. 我可以确认readImage函数没问题。 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. 我可以确认运行顺序是readImagesaveImagesetGraphic ,然后是uploadImage中的console.log,它应该具有更新的值但不具有更新的值。

  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. useState钩子正在做一些时髦的事情。 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. 该进程在启动时复制值,而不是在读取console.log时读取它,因此uploadImage函数在该进程的生命周期内获取useState挂钩的原始值。

It might become clear why this would not work if we would simplify all those React magic into : 如果我们将所有那些React魔法简化为以下内容,可能会清楚为什么这不起作用:

  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. 如您所见, setCount不会直接更改状态( count )。 It will actually not change the local count variable at all. 实际上它根本不会改变本地count变量。 Therefore "before" and "after" will always log the same (as long as you don't mutate counter directly). 因此“之前”和“之后”将始终记录相同(只要您不直接改变counter )。

Instead calling setCount will be deffered (and batched in React), and then it will recall the whole component function passing in the new count. 而是调用setCount将被保护(并在React中进行批处理),然后它将调用传递新计数的整个组件函数。

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). 因此,尝试await setState调用没有任何意义,也没有必要记录它之前和之后的状态(因为它不会在中间变化)。 Log it once in a component, if you set the state the whole component executes again and the logging will log the new state. 在组件中记录一次,如果设置状态,则整个组件再次执行,日志记录将记录新状态。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM