避免在反应组件中无限重新渲染

Posted

技术标签:

【中文标题】避免在反应组件中无限重新渲染【英文标题】:Avoiding infinite re-rendering in react component 【发布时间】:2022-01-21 07:04:46 【问题描述】:

我有一个名为 threads 的 Supabase 数据库,有 5 个条目,我正在尝试为数据库中的每一列创建一个新的 React 组件,但我遇到了一些问题。

它给我的问题是它正在停止程序以防止无限循环,我认为这是因为在我的代码中,我每次渲染我知道的组件时都会更新我的 useState,但我'不知道如何解决这个问题。

Component.js:

import useState from "react";
import supabase from "../../utils/supabaseClient";

export default function Sidebar() 
    const [newThreads, setNewThreads] = useState([]);

    // this is where I am kinda stuck
    const threadList = [];
    (async () => 
        let data: threads, error = await supabase
            .from('threads')
            .select('thread_title');
        threadList.push(threads); // how do I get the current index of the loop (and limit it to only get 5 entries?)
        threadList.forEach(function (value) 
            console.log(value)
        )
    )();
    setNewThreads(threadList);

    return (
        <div className="sidebar">
            <div className="sidebar-widget">
                <span className="widget-title">New Threads</span>
            </div>
            <div className="sidebar-widget">
                <span className="widget-title">New Members</span>
                (Object.entries(newThreads) || []).map(([key, value]) => 
                    return (
                        <div className="widget-cell">
                            <div className="widget-cell-image"/>
                            <div className="widget-cell-content">
                                <span className="widget-cell-title">value</span>
                                <div className="widget-cell-values">
                                    <span className="widget-cell-by-user">by harley_swift,</span>
                                    <span className="widget-cell-time">22:02</span>
                                </div>
                            </div>
                        </div>
                    );
                )

            </div>
        </div>
    )

任何关于解释的帮助将不胜感激!

【问题讨论】:

【参考方案1】:

解决无限重新渲染问题的方法是使用useEffect 获取数据:

useEffect(() => 
    // this is where I am kinda stuck
    const threadList = [];
    (async () => 
        let 
            data: threads,
            error
         = await supabase
            .from('threads')
            .select('thread_title');
        threadList.push(threads); // how do I get the current index of the loop (and limit it to only get 5 entries?)
        threadList.forEach(function(value) 
            console.log(value)
        )
    )();
    setNewThreads(threadList);
, []); // note the empty dependency list

空的依赖列表是你知道效果只会运行一次的方式之一。

要仅将 5 个项目添加到列表中,您可以对结果进行切片(或检查 supabase 是否具有这样做的内置方法):

threadList.push(threads.slice(0, 5));

最后,请注意状态更改是异步的,在您的情况下更是如此,因为您实际上是通过网络进行获取。

如果您想在newThreads 更新时收到通知,您可以使用另一个useEffect,如下所示:

useEffect(() => 
  if (newThreads) 
    console.warn(newThreads.length);
  
, [newThreads]);

if 语句并不是真正需要的,但为了完整性而添加

这也适用于当你想实际渲染newThreads时,你需要使用条件渲染来做到这一点:


  (Object.entries(newThreads) || []).map(([key, value]) => 
    return (
      <div className="widget-cell">
        <div className="widget-cell-image" />
        <div className="widget-cell-content">
          <span className="widget-cell-title">value</span>
          <div className="widget-cell-values">
            <span className="widget-cell-by-user">by harley_swift,</span>
            <span className="widget-cell-time">22:02</span>
          </div>
        </div>
      </div>
    );
  ) || <div>Loading...</div>

【讨论】:

绝对的救命稻草,谢谢! 我试过实际实现这段代码,看起来一切正常,但发生了一件事情:当我打印 newThreads 的长度时,它有时返回 0,有时返回 5,有时返回组件不要渲染(在 Object.entries 部分)。你知道为什么吗? (看起来它没有在初始渲染时渲染) @HarleySwift 这取决于您在哪里打印长度。状态变化不是同步的。如果您在错误的时间检查状态,您可能会看到与等待更长时间不同的情况。至于未渲染的组件,也可能与列表未按时填充有关。您可以在进行渲染之前添加检查以查看列表是否已准备好。我会更新我的答案 我只是做了一些实验,我硬编码了 arraylist 值,它工作得很好。您认为这与延迟/损坏的数据库检索有关吗?有没有办法我应该创建一个每 5 分钟更新一次的新列表,而不是每次都拉取它? @HarleySwift 是的,这绝对与必须先获取数据的延迟有关。我已经更新了我的答案。请在 *** 上提出一个新问题,以免我们从您的原始问题中脱离主题。

以上是关于避免在反应组件中无限重新渲染的主要内容,如果未能解决你的问题,请参考以下文章

如何避免在反应功能组件中对“静态组件”进行不必要的重新渲染?

反应:停止映射组件的重新渲染

React 限制渲染次数以防止无限循环...重新渲染次数过多

从消费者 componentDidMount 更新反应上下文会导致无限重新渲染

Vue中重新渲染组件的方法总结

反应我必须重新渲染父母才能重新渲染孩子吗?