如何将函数传递给自己的反应上下文提供程序,让您编辑状态?

Posted

技术标签:

【中文标题】如何将函数传递给自己的反应上下文提供程序,让您编辑状态?【英文标题】:How pass a function into an own react context provider which lets you edit the state? 【发布时间】:2020-01-10 11:07:01 【问题描述】:

我有以下代码:

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>)

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

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 中的那样更新。我怎样才能防止这种情况发生?实现这一点的设计模式是什么样的?

【问题讨论】:

把整个代码,不是sn-ps,是addNumber调用在组件函数体中吗? 没错,它们位于自定义 NumberProvider 组件内部的组件函数体中 请更新您的问题,显示整个代码,预期的行为是什么,为什么它在函数体中?你想计算渲染次数吗?详细说明。 【参考方案1】:

因为useState 的设置器是异步的,所以一个调用会覆盖另一个调用:

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>;
;

您应该使用functional useState:

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

正如 cmets 中所讨论的,您应该使用 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>
  )

【讨论】:

使用功能性useState,也尝试解释一下你想要做得更好的地方,没有更多信息很难提出建议 我真正想要实现的是在我的应用程序中为 toasts(通知消息)创建一个消息队列。祝酒词应该被推入队列并在一段时间后被删除。我想通过上下文 api 从代码中的任何地方推送它们。在另一个地方,我想映射队列并显示当前在此队列中的所有消息。 这是我的实际代码库:github.com/blicc-org/blicc/blob/master/app/src/common/context/…,我的主要问题是我的自定义上下文提供程序中的 useState 似乎没有正确更新 你应该使用 useEffect 的副作用并从那里的队列中删除。 我更新了答案,请参考How do I ask a good question?

以上是关于如何将函数传递给自己的反应上下文提供程序,让您编辑状态?的主要内容,如果未能解决你的问题,请参考以下文章

如何将对象上下文传递给 jQuery.ajax JSONP 回调?

如何将上下文传递给提供者的第二个小部件树

更新后如何将函数传递给 SetState 回调? (反应)

如何将 this 上下文传递给函数?

如何从反应原生桥将参数传递给swift类

如何将查询参数传递给 Rails API 控制器?