如何使用useState在React功能组件中的循环中正确设置状态

时间:2020-06-04 12:52:25

标签: javascript reactjs closures react-hooks

考虑一下,我正在上传一堆文件,并且试图跟踪正在进行的文件,因此,每当上传完成时,我都会减小状态,直到所有操作都完成,最后将上传状态更改为{ {1}}: (在这里,我做了一个false函数来模仿上传过程)。

sleep

问题出在import React, { useState} from "react"; const sleep = (ms, dev =1 ) => { const msWithDev = (Math.random() * dev + 1) * ms return new Promise(resolve => setTimeout(resolve, msWithDev)) } function Counter() { const [, setSortEnabled] = useState(true) const [count, setCount] = useState(0) const arr = [1, 2, 3, 4, 5] const upload = async () => { await sleep(1000) setCount(c => c - 1) console.log(`count state: ${count}`) if (count === 0) { console.warn('Sort set to enabled again.') setSortEnabled(true) } } const onClick = () => { console.warn('Sort set to disabled.') arr.forEach(item => { setCount(c => c + 1) console.error(count) upload() }) } return ( <> <button onClick={onClick}>Click me!</button> </> ); } export default Counter;

upload

始终记录function `console.log(\`count state: ${count}\`)` ,这意味着状态从未更新过。

我在做什么错了?

2 个答案:

答案 0 :(得分:1)

这意味着状态从未更新

它确实已更新,您只是在记录陈旧的值 because of closures

闭包是捆绑在一起(封闭)的函数和对其周围状态(词汇环境)的引用的组合。换句话说,闭包使您可以从内部函数访问外部函数的作用域。

要解决此问题,请使用FROM ubuntu:18.04 functional updates

setState

Edit spring-sky-sw8gv

答案 1 :(得分:1)

您必须使用Promise.all来等待所有文件上传,而不是依靠计数。在您的情况下,计数是从关闭接收的,因此即使状态已更新,计数变量也不受影响。

您可以使用Promise.all like

实现上述逻辑
function Counter() {
  const [, setSortEnabled] = useState(true)
  const arr = [1, 2, 3, 4, 5]

  const upload = async () => {
    await sleep(1000);
  }


  const onClick = async ()  => {
    console.warn('Sort set to disabled.')
      const promises = arr.map(item => {
        console.error(count)
        return upload()
      });
      await Promise.all(promises);
      console.warn('Sort set to enabled again.')
      setSortEnabled(true)
    }

  return (
    <>
    <button onClick={onClick}>Click me!</button>
    </>
  );
}

export default Counter;

更新:

要解决count状态的关闭问题,可以使用refs和useEffect。但是,这是一种解决方法,因此不应该被优先使用

import React, { useState} from "react";

const sleep = (ms, dev =1 ) => {
  const msWithDev = (Math.random() * dev + 1) * ms
  return new Promise(resolve => setTimeout(resolve, msWithDev))
}

function Counter() {
  const [, setSortEnabled] = useState(true)
  const [count, setCount] = useState(0)
  const arr = [1, 2, 3, 4, 5]
  const countRef = useRef(count);
  useEffect(() => {
     countRef.current = count;
  }, [count ]);
  const upload = async () => {
    await sleep(1000)
    setCount(c => c - 1)

      console.log(`count state: ${count}`)

      if (countRef.current === 0) {
        console.warn('Sort set to enabled again.')
        setSortEnabled(true)
    }
  }


  const onClick = ()  => {
    console.warn('Sort set to disabled.')
      arr.forEach(item => {
        setCount(c => c + 1)
        upload();
      })
    }

  return (
    <>
    <button onClick={onClick}>Click me!</button>
    </>
  );
}

export default Counter;