避免在 React 中由对象字面量引起的重新渲染:如何处理对象中的变量?

Posted

技术标签:

【中文标题】避免在 React 中由对象字面量引起的重新渲染:如何处理对象中的变量?【英文标题】:Avoid rerender in React caused by objects literal : how to do with variables in the object? 【发布时间】:2019-07-11 23:19:14 【问题描述】:

我在这篇文章React is Slow, React is Fast: Optimizing React Apps in Practice 中读到:

实际上,每次将对象字面量作为 prop 传递给子组件时,都会破坏纯度。

好的,我知道了。所以最好避免这种情况是用对象创建一个变量,并将这个变量插入到道具中,就像这样:

import React from 'react';

const style =  marginTop: 10 ;
const AnyComponent = (props) => (
    <div style=style>
        ...
    </div>
)

但是如果 style 属性依赖于接收到的属性呢?对象应该在哪里? 例如,我有这个组件:

import React from 'react';

const AnyComponent = (props) => (
    <div style= marginTop: props.marginTop >
        ...
    </div>
)

这是一个好习惯吗:

import React from 'react';

const style = (marginTop) => ( marginTop )
const AnyComponent = (props) => (
    <div style=style(props.marginTop)>
        ...
    </div>
)

[编辑] 我忘了说我的大多数组件都有状态,所以在这种情况下,这样做是个好主意吗:

import React from 'react';

class App extends React.Component 

  style = () => (
    marginTop: this.props.marginTop
  )

  render() 
    return(
      <div style=this.style()>

      </div>
     )
  

【问题讨论】:

你把事情复杂化了。您有性能问题吗? 我实际上是这样做的:我的应用程序非常大(比这大得多),使用 redux,并且我的一个深层嵌套组件使用了 Draft-js。为了方便起见,我想将 EditorState 存储在我的 redux 商店中,但由于性能和太多的重新渲染问题导致文本编辑器滞后,我现在不能这样做。我想试一试这个优化,因为我已经做了很多(重组、重新选择等) 【参考方案1】:

以前您无法在函数式组件中执行此操作(尽管您可以使用 memoization) 但是现在借助 React 钩子,您可以执行以下操作:

const AnyComponent = (props) => 
    const style = useMemo(() => ( marginTop: props.marginTop ), [props.marginTop]);
    <div style=style>
        ...
    </div>

不,你不能用这个:

import React from 'react';

const style = (marginTop) => ( marginTop )
const AnyComponent = (props) => (
    <div style=style(props.marginTop)>
        ...
    </div>
)

因为它还会在每次重新渲染 AnyComponent 时通过调用 style 函数创建一个新对象。

【讨论】:

useMemo 返回记忆值,而不是返回它的函数。 getStyle 的建议适用于 useCallback @estus 非常感谢您的评论。我会编辑我的答案【参考方案2】:

您可以在组件中创建变量,如下所示:

import React from 'react';

const AnyComponent = (props) => 
 // if props.marginTop is an object
 const style = props.marginTop;

 return (
    <div style=style>
        ...
    </div>
);

【讨论】:

这是不是每次都创建一个新的style 变量?之前的 style 和当前的 style 对象会有不同的地址,对吧?【参考方案3】:

一个对象可以用useMemo钩子来记忆:

const AnyComponent = (( marginTop ) => (
    const style = useMemo(() => ( marginTop ), [marginTop]);
    <div style=style>
        ...
    </div>
)

由于useMemo 旨在进行昂贵的计算并且有其自身的开销,因此对于div 情况,这可能被视为过早优化。

【讨论】:

感谢您的回复,您对类组件有什么建议?我用提案编辑了我的帖子,你怎么看? useMemo 特定于功能组件。你可以使用像 Lodash 这样的通用记忆器。正如我所提到的,这种优化对 没有多大意义。对于带有自定义 props 的类组件,更好的做法是让它们负责重新渲染决策,并使用 shouldComponentUpdate。 @estus 你能告诉它需要什么昂贵的计算吗? @EddieCooro 一些占用大量 CPU 或 RAM 的函数,例如复杂对象对象。

以上是关于避免在 React 中由对象字面量引起的重新渲染:如何处理对象中的变量?的主要内容,如果未能解决你的问题,请参考以下文章

React Context API 并避免重新渲染

使用 React 钩子 useContext 避免不必要的重新渲染

使用重新选择计算派生状态时如何避免 React 重新渲染

避免在 React 渲染循环中进行每次迭代绑定

探究React虚拟DOM

如何使用 react-hooks (useEffect) 缓冲流数据以便能够一次更新另一个组件以避免多次重新渲染?