为什么我的 React 子组件会重新渲染

Why is my React subcomponent re-rendering

我有这个应用程序,我在页面加载时提取大量数据,然后使用 react-tables 呈现 table。每行都有一个合规性列,其中包含有关是否合规的状态标签。此列还包含一个按钮,按下该按钮会使用推荐的合规值更新状态 (queuedPortRecommendations)。

table 需要很长时间才能呈现,正如预期的那样,它大约有 1400 行。但是我不明白为什么它会在 addQueuedChange 函数被调用时重新渲染?

我尝试从 useMemo 的 deps 数组中删除 addQueuedChange,但随后并没有向数组中添加字符串,而是将其替换。

我知道很难想出一个直接的答案,但如果有任何提示或想法,我将不胜感激。

App.tsx

const App = () => {
    const [data, setData] = useState<Port[]>([]);
    const [queuedPortRecommendations, setQueuedPortRecommendations] = useState<string[]>([]);
  
    useEffect(() => {
      const doFetch = async () => {
        const response = await fetch(`${BACKEND_BASE}/api/v1/ports?allData=false`);
        const body: BackendResponse  = await response.json()
        const { ports, portStats } = body.data;
        setData(ports);
      }
      doFetch()
    }, [data]);
  
    const addQueuedChange = (portRecommendation: string) => {
      setQueuedPortRecommendations([...queuedPortRecommendations, portRecommendation]);
    };
  
    const columns = useMemo(
      () => [
        {
          Header: 'Device name',
          accessor: 'device.hostname'
        },
        {
          Header: 'Compliance checks',
          accessor: (port: Port) => {
            return (
              <PortDescriptionRecommendation
                key={`${port.device}-${port.ifName}-port-recommendation`}
                port={port}
                addQueuedChange={addQueuedChange}
              />
            );
          },
        }
      ],
      [addQueuedChange]
    )
  
  return <TableContainer columns={columns} data={data} />;

PortDescriptionRecommendation

const PortDescriptionRecommendation = ({ port, addQueuedChange }: {port: Port, addQueuedChange:  (portRecommendation: string) => void }) => {

  const {remoteHostName, remotePort, tag, type} = port.portRecommendation;
  const remotePortDescription = remotePort ? ` (${remotePort})` : '';

  const handleAddToQueueButtonClicks = () => {
    addQueuedChange(portConfig);
  }

  let portConfig = `
conf t
interface ${port.ifName}
description ${tag}${remoteHostName || ''}${remotePortDescription}
end
wr mem`;

  return (
        <button data-tip={portConfig} className={'btn btn-sm btn-info'} onClick={handleAddToQueueButtonClicks}>
          Add recommendation to list
        </button>
   );
}

您应该使用 useCallback 来避免 re-rendering,每次您更新应用状态时,都会创建一个新的 addQueuedChange 实例。这就是为什么。而且你不必将它添加到 dependencies 数组。

首先,您想将 useCallback 添加到 addQueuedChange,这样它就不会在每次渲染时创建新的 columns

虽然它可能无法完全解决问题: 当App分量变为re-renders时。 TableContainer 也是 re-rendered。这是默认的 React 行为:当父级更新时,它 re-renders 所有子级。要更改此行为,您应该使用 React.memo.

一般来说,当您使用大 table 时,您希望使用 React.memo。根据我的经验,我通常也会记住 table 的行。

确保 addQueuedChange 不依赖于 portRecommendation

const addQueuedChange = useCallback(
  (portRecommendation: string) => {
    setQueuedPortRecommendations((prevPortRecommendation) => [
      ...prevPortRecommendation,
      portRecommendation,
    ]);
  },
  [setQueuedPortRecommendations]
);