使用 useCallback 响应无限更新循环(react-hooks/exhaustive-deps) [英] React infinite update loop with useCallback (react-hooks/exhaustive-deps)

查看:40
本文介绍了使用 useCallback 响应无限更新循环(react-hooks/exhaustive-deps)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下呈现 iframe 列表的示例.

我想将渲染的 iframe 的所有 document 存储在 frames 中.

import React, { useState, useEffect, useCallback } from "react";import Frame, { FrameContextConsumer } from "react-frame-component";函数 MyFrame({ id, document, setDocument }) {useEffect(() => {console.log(`为 ${id} 设置文档`);setDocument(id, 文档);}, [id, 文档]);//注意:将 `setDocument` 添加到数组会导致无限更新循环!返回<h1>{id}</h1>;}导出默认函数 App() {const [frames, setFrames] = useState({桌面: {名称:桌面"},移动的: {名称:《手机》}});const setFrameDocument = useCallback((id, 文件) =>{设置帧({...帧,[ID]: {...帧[id],文档}});},[帧数,设置帧数]);控制台日志(帧);返回 (<div className="应用程序">{Object.keys(frames).map(id => (<帧键={id}><FrameContextConsumer>{({文档}) =>(<我的框架id={id}文档={文档}setDocument={setFrameDocument}/>)}</FrameContextConsumer></帧>))}

);}

这里有两个问题:

  1. react-hooks/exhaustive-deps 抱怨依赖项数组中缺少 setDocument.但是,添加它会导致无限更新循环.
  2. 控制台日志frames 显示只设置了移动设备的document.我希望桌面的 document 也能设置.

你会如何解决这个问题?

Codesandbox

解决方案

const setFrameDocument = useCallback((id, 文件) =>setFrames((frames) => ({...帧,[ID]: {...帧[id],文档}})),[]);

https://codesandbox.io/s/gracious-wright-y8esd


由于状态的更新,frames 对象的引用不断变化.使用之前的实现(即依赖数组中的frames 对象),它会引起连锁反应,导致组件重新渲染并导致frames 对象获得新的引用.这将永远持续下去.

仅使用 setFrames 函数(常量引用),此链式反应不会传播.eslint 知道 setFrames 是一个常量引用,因此它不会向用户抱怨它从依赖项数组中丢失.

Consider the following example that renders a list of iframes.

I'd like to store all the documents of the rendered iframes in frames.

import React, { useState, useEffect, useCallback } from "react";
import Frame, { FrameContextConsumer } from "react-frame-component";

function MyFrame({ id, document, setDocument }) {
  useEffect(() => {
    console.log(`Setting the document for ${id}`);
    setDocument(id, document);
  }, [id, document]); // Caution: Adding `setDocument` to the array causes an infinite update loop!

  return <h1>{id}</h1>;
}

export default function App() {
  const [frames, setFrames] = useState({
    desktop: {
      name: "Desktop"
    },
    mobile: {
      name: "Mobile"
    }
  });
  const setFrameDocument = useCallback(
    (id, document) => {
      setFrames({
        ...frames,
        [id]: {
          ...frames[id],
          document
        }
      });
    },
    [frames, setFrames]
  );

  console.log(frames);

  return (
    <div className="App">
      {Object.keys(frames).map(id => (
        <Frame key={id}>
          <FrameContextConsumer>
            {({ document }) => (
              <MyFrame
                id={id}
                document={document}
                setDocument={setFrameDocument}
              />
            )}
          </FrameContextConsumer>
        </Frame>
      ))}
    </div>
  );
}

There are two issues here:

  1. react-hooks/exhaustive-deps is complaining that setDocument is missing in the dependency array. But, adding it causing an infinite update loop.
  2. Console logging frames shows that only mobile's document was set. I expect desktop's document to be set as well.

How would you fix this?

Codesandbox

解决方案

const setFrameDocument = useCallback(
    (id, document) => setFrames((frames) => ({
      ...frames,
      [id]: {
        ...frames[id],
        document
      }
    })),
    []
  );

https://codesandbox.io/s/gracious-wright-y8esd


The frames object's reference keeps changing due to the state's update. With the previous implementation (ie the frames object within the dependency array), it would cause a chain reaction that would cause the component to re-render and causing the frames object getting a new reference. This would go on forever.

Using only setFrames function (a constant reference), this chain react won't propagate. eslint knows setFrames is a constant reference so it won't complain to the user about it missing from the dependency array.

这篇关于使用 useCallback 响应无限更新循环(react-hooks/exhaustive-deps)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆