2

我有以下代码:

import React, { useState } from 'react'

export const NumberContext = React.createContext()

export function NumberProvider({ children }) {
  const [numbers, setNumbers] = useState([])

  function addNumber(number) {
    const copy = [...numbers]
    copy.push(number)
    setNumbers(copy)
  }

  return (
    <NumberContext.Provider value={[numbers, addNumber]}>
      {children}
    </NumberContext.Provider>
  )
}
export function App() {
  return (
      <NumberProvider>
        <DisplayNumbers />
        <AlterNumbers />
      </NumberProvider>
  )
}
export function DisplayNumbers() {
  const [numbers, addNumbers] = useContext(NumberContext)

  return (<p>{numbers}</p>)
}

如果我现在addNumber()像这样通过提供程序中的 Context api 调用该函数

export function AlterNumbers() {
  const [numbers, addNumber] = useContext(NumberContext)

  function alterNumbers(){
    addNumber(1)

    setTimeout(() => {
      addNumber(2)
    }, 3000)
  }

  return (<button onClick={alterNumbers}>)
}

numbers等于[2]而不是[1, 2]

我的提供程序函数中的状态NumberProvider不会像NumberContext.Provider. 我怎样才能防止这种情况发生?实现这一点的设计模式会是什么样子?

4

1 回答 1

2

因为 setteruseState是异步的,所以一个调用会覆盖另一个调用:

const NumberConsumer = () => {
 const [numbers, addNumber] = useContext(NumberContext);

 addNumber(1);               // Will update numbers to [1] in "future"
 setTimeout(() => {          // Will update numbers to [2] in "future"
    addNumber(2);            // The call within the timeout,
                             // is not aware of the previous state
 }, 3000);

 return <h1>{JSON.stringify(numbers)}</h1>;
};

您应该使用功能useState

setState(prevState => {
  // Object.assign would also work
  return {...prevState, ...updatedValues};
});

正如评论中所讨论的,您应该将逻辑更改为更健壮的东西useEffect

export function ToastProvider({ children }) {
  const [toasts, setToasts] = useState([])
  const [key, setKey] = useState(0)

  function showToast(label, message) {
    setKey(key + 1)
    setToasts([
      ...toasts,
      {
        key,
        label,
        message,
      },
    ])
  }

  useEffect(() => {
    setTimeout(() => {
      const copy = [...toasts]
      copy.pop()
      setToasts(copy)
    }, 5000)
  }, toasts);

  return (
    <ToastContext.Provider value={[toasts, showToast]}>
      {children}
    </ToastContext.Provider>
  )
}
于 2019-09-08T11:32:08.607 回答