反应useContext不会导致重新渲染

时间:2020-07-10 15:31:53

标签: javascript reactjs react-hooks react-context

因此,我尝试使用利用useContext的自定义钩子,当ToastContext更改时,我希望该钩子会重新渲染ToastContainer组件。但是,当上下文更改时,ToastContainer组件不会重新呈现。当使用开发工具时,我可以看到该上下文确实已由该钩子更改,但是未显示新数据。

对不起所有代码,我只是不确定错误在哪里

useToast.js

function useToast () {
  let [toasts, setToasts] = useContext(ToastContext)
  

  function createToast(message, color, duration = 0) {
    let id = Math.floor(Math.random() * 1000)
    toasts.set(id, <Toast {...{ message, color, duration, id }} />)
    setToasts(toasts)

    if (duration) {
      setTimeout(() => { toasts.delete(id); setToasts(toasts)}, duration * 1000)
    }
  }

  return [toasts, createToast]
}

ToastContainer.js

function ToastContainer (props) {
  let [toasts, setToasts] = useContext(ToastContext)
  return( <> {[...toasts.values()]} </>)
}

page.js

function Page (props) {  
    let [toasts, createToast] = useToast()  
    createToast("hello", 'red')
    createToast("world", 'yellow')

    return(<Article />)
}

app.js

function App({Component, pageProps}) {


  const toastState = useState(new Map())

  return (
    <>
          <ToastContext.Provider value={toastState}>
            <ToastContainer/>
            <main>
                <Component {...pageProps}></Component>
            </main>
          </ToastContext.Provider>
    </>
  )

1 个答案:

答案 0 :(得分:1)

几件事:

通过调用toasts.set(id, <Toast {...{ message, color, duration, id }} />),您可以直接更改不需要的状态。然后,您使用完全相同的setToasts对象调用Map,因为它是相同的引用,所以它不会触发重新渲染。

如果这可行,则通过在呈现的功能组件中调用createToast()来触发Maximum update depth exceeded异常,就像这样:

  • 已渲染
  • 进行了烤面包,触发了重新渲染
  • 重新渲染
  • 进行了烤面包,触发了重新渲染
  • 重新渲染 ...等等

您应该通过单击按钮或其他有意义的操作将Toast的创建移至事件驱动。

您可以使用Map,但是您需要执行以下操作:

const [myMap, setMyMap] = useState(new Map());
const updateMap = (k,v) => {
  setMyMap(new Map(myMap.set(k,v)));
}

,如https://medium.com/swlh/using-es6-map-with-react-state-hooks-800b91eedd5f所示。这将使用当前Map的键值对创建一个新的Map对象。

或者,您可以使用对象{},并进行一些调整:

const toastState = useState({});

setToasts({
  ...toasts,
  [id]: <Toast key={id} {...{ message, color, duration, id }} />
});

function ToastContainer (props) {
  let [toasts, setToasts] = useContext(ToastContext)
  return Object.values(toasts);
}

if (duration) {
  setTimeout(() => {
    const newToasts = { ...toasts };
    delete newToasts[id];
  }, duration * 1000)
}