useState数组和异步作用域查找

时间:2020-08-30 20:35:05

标签: reactjs asynchronous scope

考虑依赖于两个异步调用的组件的状态。一个是初始呼叫,另一个是重复呼叫。 两者都更改由useState控制的数组。通过将旧数组散布到文字中来附加数组。

问题是当两个调用都解析它们设置相同的状态时,但是与旧数组的传播是从调用useEffect开始的作用域查找。因此,其中一个调用总是“覆盖”另一个。

这是一些简化的代码:

const Component = () => {
  const location = useLocation()

  const [numbers, setNumbers] = useState([1,2,3])

  // say this returns [4,5,6]
  useEffect(()=>{
    fetchOnce().then(newNumbers => setNumbers([...numbers, newNumbers]))
  }, [])

  // say this returns [7,8,9]
  useEffect(()=>{
    fetchMultiple().then(newNumbers => setNumbers([...numbers, newNumbers]))
  }, [location])
}

最终结果是[1,2,3,4,5,6]或[1,2,3,7,8,9]。

如何更改它以便正确设置状态? 我应该是[1,2,3,4,5,6,7,8,9]或[1,2,3,7,8,9,4,5,6]。

1 个答案:

答案 0 :(得分:2)

改为使用setNumbers的回调形式:

const Component = () => {
  const location = useLocation()

  const [numbers, setNumbers] = useState([1,2,3])

  // say this returns [2,3,4]
  useEffect(()=>{
    fetchOnce().then(newNumbers => setNumbers(numbers =>[...numbers, ...newNumbers]));
  }, [])

  // say this returns [5,6,7]
  useEffect(()=>{
    fetchMultiple().then(newNumbers => setNumbers(numbers => [...numbers, ...newNumbers]))
  }, [location])
}

const { useState, useEffect } = React;
const fetchOnce = () => Promise.resolve([2, 3, 4]);
const fetchMultiple = () => Promise.resolve([5, 6, 7]);
console.error = () => void 0; // suppress key warning, not sure what OP wants for keys

const Component = () => {
  const [numbers, setNumbers] = useState([1,2,3])
  useEffect(()=>{
    fetchOnce().then(newNumbers => setNumbers(numbers =>[...numbers, ...newNumbers]));
  }, [])
  useEffect(()=>{
    fetchMultiple().then(newNumbers => setNumbers(numbers => [...numbers, ...newNumbers]))
  }, [location])
  return (
    <div>
      {numbers.map(number => <div>{number}</div>)}
    </div>
  );
}

ReactDOM.render(<Component />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class="react"></div>