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

Posted

技术标签:

【中文标题】如何避免在反应功能组件中对“静态组件”进行不必要的重新渲染?【英文标题】:How to avoid unnecessary re-render for "static components" in react functional component? 【发布时间】:2020-05-04 13:26:23 【问题描述】:

我有一个反应功能组件,显示标签和帖子列表 + 一些静态文本/装饰。我使用useState 钩子将当前选定的标签存储在一个状态中。使用 apollo 的 useQuery 钩子和 tag 变量获取帖子。用户应该能够选择一个标签,它将替换当前的tag 状态 - 因此useQuery(POSTS_QUERY) 将使用新的tag 变量重新运行。

const onTagSelectChange = (window: Window, 
    router: NextRouter, 
    name: string, 
    checked: boolean,
    tagSetter: React.Dispatch<React.SetStateAction<string>>) => 

    if (checked) 
        setTagQueryInUrl(window, router, name)
        tagSetter(name)
     else 
        setTagQueryInUrl(window, router, null)
        tagSetter(null)
    


const NewsList: NextPage = () => 

    const router = useRouter()
    const query = router.query as Query

    // store tag in state
    // initialize tag from `tag` query
    const [tag, setTag] = useState(query.tag)

    const  data: postsData, loading: postsLoading, error: postsError  = useQuery(
        POSTS_QUERY,
        
            variables: 
                tag: tag
            
        
    )

    const  data: tagsData, loading: tagsLoading, error: tagsError  = useQuery(TAGS_QUERY)

    // show error page if either posts or tags query returned error
    if (postsError || tagsError) 
        return <Error statusCode=500 />
    

    return (
        <div>
            <h1>Here we have list of news, and I should not re-render everytim :(</h1>
            <Tags
                loading=tagsLoading 
                data=tagsData 
                isChecked=(name) => name === tag 
                onChange=(name, checked) => onTagSelectChange(window, router, name, checked, setTag)
            />
            <Posts loading=postsLoading data=postsData />
        </div>
    )

我的问题是,为什么我的 h1 块会不断重新渲染,即使我没有传递任何东西给它?还是我完全误解了 react 的工作原理?

【问题讨论】:

您应该检查元素面板以查看&lt;h1&gt; 元素是否实际被更改。您看到的重新渲染可能是因为 &lt;h1&gt; 元素的兄弟姐妹正在被修改,因此它们的共享父元素得到了重排。您还可以通过将&lt;h1&gt; 移到&lt;div&gt; 之外来测试它,这样它就不再是同级了。 这能回答你的问题吗? Trace why a React component is re-rendering @EmileBergeron 不,我没有将任何道具传递给 &lt;h1&gt; 元素,因此不适用于我的情况。 但是封闭的组件会重新渲染,包括它的所有子组件。 @Wex 我看看检查员,是的,你是对的,&lt;h1&gt; 没有被修改。看起来&lt;body&gt; 是被修改的那个,有一个&lt;iframe&gt; 被添加了非常快的毫秒但我不记得添加任何&lt;iframe&gt; 【参考方案1】:

React 组件在其状态或道具发生变化时重新渲染。如果我没看错,那么只要 url 发生变化,你就会改变状态中的标签,从而使组件重新呈现自己。

【讨论】:

是的,我正在将我的tag 状态更改为onTagSelectChange。但是其他没有收到任何 props 的组件难道不应该重新渲染吗?【参考方案2】:

由于您的状态已在您的 NewsList 组件上声明,因此任何状态更改(正如另一个用户在他的回答中所述)都将触发 整个组件(NewList) 的重新渲染,而不仅仅是传递给您传递状态的组件(因此传递给您在那里的静态&lt;h1&gt;)。

如果该组件的某些部分与此状态无关,您可以将它们移到外部以避免重新渲染。

虽然,在这样的情况下,重新渲染你的 &lt;h1&gt; 对 React 来说不是成本。对于发生更复杂事情的自定义组件(例如填充列表或计算内容等),您应该担心并遵循这种方法。在这些情况下,如果这些复杂的事情不受父状态更改的影响,您不希望所有这些复杂的事情再次发生。您还应该始终考虑,如果将组件移到外部是有意义的,或者这样做会使您的代码变得复杂。

您应该始终在组织良好和高效的代码之间取得平衡。

【讨论】:

以上是关于如何避免在反应功能组件中对“静态组件”进行不必要的重新渲染?的主要内容,如果未能解决你的问题,请参考以下文章

更改值后反应组件未更新

如何设置反应组件的 iframe 内容

当应用程序从后台处于活动状态时,如何避免在本机反应中安装组件?

检查 cookie 中 JWT 令牌的有效性时如何避免不必要的 API 调用

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

如何在反应原生功能组件中制作动态选择器?