React Redux 和 Should 组件更新优化

React Redux and Should Component Update Optimisation

代码:

根组件

    <Provider store={Store}>
       <AppLayoutContainer>
           {this.props.template}
       </AppLayoutContainer>
    </Provider>

AppLayoutContainer

....some code

  return(
    <div className={`AppLayoutContainer`}>
       <Modal isOpen={isModalShown} .... />
       <div>{children}</div>
     </div>
  )

....some code

function mapStateToProps(state) {
  const { app } = state;
  return {
    isModalShown: app.isModalShown
   }
}

export default connect(mapStateToProps)(AppLayoutContainer);

问题:

每次 Applayoutcontainer 从 Redux 获取 isModalShown 时,我的整个应用程序都在重新渲染,这非常糟糕。我只需要重新渲染 Modal 组件,不要碰我的 children。我觉得用 ShouldComponentUpdate 方法是可以的,但是我真的不明白该怎么做。

有什么想法吗?我知道有人在他的项目中做过,可以为此建议一些最佳实践。谢谢


更新:

阅读所有答案,是的,将 Modal 与 Store 连接是一个很好的解决方案, 但是如果

1) 我希望 Modal 组件是纯的(可重复使用的)组件?

2) 我需要在 AppLayoutContainer 中使用 isModalShown ?

return(
    <div className={`AppLayoutContainer`}>
       <Modal isOpen={isModalShown} .... />
       <div>{children}</div>
     </div>
  )

更新 2.0

谢谢你的回答,但我能用这个做什么

显示 Modal 组件时我需要模糊所有内容的主要问题,据我所知 blur() 只能应用于内容才能工作(不能应用于绝对位置 div 覆盖我的内容)并像这样

return(
        <div className={`AppLayoutContainer`}>
           <Modal isOpen={isModalShown} .... />
           <div className={isModalShown ? 'blured' : null }>{children}</div>
         </div>
      )

但是每次我显示 Modal 时我的整个应用程序都会重新呈现,我需要冻结它。为 child 构建容器并在此处执行 shouldcomponentupdate 的解决方案并不好,React 如何比较整个组件?它只比较 props 中的非不可变字段。

有什么解决办法吗?

这里有不同的策略。

  1. 在您的子组件中使用 shouldComponentUpdate(在这种情况下非常混乱和不雅);
  2. 将 Modal 组件连接到商店而不是 AppLayoutContainer ;
  3. isModalShown存储在Modal的状态,而不是全局状态。

如果可能,最后的解决方案通常是更合适的恕我直言。您在相关组件中保持 UI 状态,因为您不需要经常在其他组件中意识到这一点。

class Modal extends Component {
  state = {
    isModalShown: false,
  };

  render() {
    // ...
  }
}

如果你需要在其他地方知道isModalShown,你应该连接Modal组件而不是AppLayoutContainer

export default connect((state) => ({
  isModalShown: state.app.isModalShown,
}))(Modal)

您应该做的不是在 AppLayoutContainermapStateToProps 函数中传递 isModal,而是 connect Modal 本身。

mapStateToProps 更改的任何结果都将导致 connected 组件重新呈现。在您的情况下,您的 mapStateToProps connect 编辑为 AppLayoutContainer returns isModalShown 的值,这导致您的 AppLayoutContainer 重新呈现。

如果您只想重新渲染 Modal 本身,那么只需

export default connect(state => ({isOpen: state.app.isModalShown}))(Modal)

在您的 Modal 文件中,如果您更喜欢,也可以在单独的文件中。

同时从 AppLayoutContainer 中删除 isModalShown 属性,因为你不需要它 因为把它留在那里意味着 AppLayoutContainer每次 isModalShown 更改时仍会重新呈现(这是您要修复的问题)。

关于问题的更新

问题 1:

你可以拥有一个纯粹的Modal

export default function Modal(props) {
  return <span>Open={props.isOpen}</span>;
}

然后您可以为特定用途制作特定模态框:

import Modal from './modal';
import { connect } from 'react-redux';

export default const AppLayoutModal = connect(state => ({isOpen: state.app.isModalShown}))(Modal);

您可以对各种单独的模态框执行多次此操作。事实上,这就是为什么使用纯组件是个好主意。

问题 2:

如果你想在 AppLayoutContainer 中使用 isModalShown 那么你有一些选择:

  • 随便用。 React 总是渲染整个组件。因为它只是一种渲染方法。它会更新 DOM,如果有的话,更多 fine-grained 但是如果你想在发生变化时渲染更少的东西,那么你可以让你的渲染函数变小。如果您让组件变大,那么整个组件都会重新渲染。
  • 您可以将组件分解成更小的组件,以便它们可以单独呈现。您可以让一个小组件直接通过 connect 使用 isModalShown,这样大的 AppLayoutContainer 就不会改变。 Child 组件可以独立于其 parent 组件进行更新。

更新 2

要模糊整个应用程序,只需在应用程序中添加一个 class。 Child 组件不会重新渲染。 Redux 阻止 children 渲染,因为它注意到 children 没有使用 isModalShown。生成的 React 元素与之前的渲染完全相同 object(参考)。 React 看到它是相同的元素并且知道不更新整个树。这是可行的,因为 React 元素是不可变的。您的整个应用程序不会重新呈现。事实上,React 只会 添加或删除 class 属性中的 class。

如果您的 children 不使用 Redux 来防止渲染在树上走动,那么您可以将 children 包装在 Redux 连接组件中。或者,您可以使用 shouldComponentUpdate 来防止渲染 children.

Bare React 要求您从根部向下发送数据。 Redux 要求您将数据注入树中需要的地方。后者的好处是不需要重新渲染任何其他内容。如果没有 Redux,整个树将 总是 在每次更改时重新渲染,除非组件使用 shouldComponentUpdate 停止传播。但是,这不仅会停止当前组件的渲染,还会停止所有 children 的渲染。 Redux 消除了这一点,因为 child 组件可以在没有 parent 的合作或知识的情况下更新它们的道具。

简而言之:到处使用 Redux 以提高速度。

我认为 DDS 和 Anthony Dugois 有权这样做,但如果这些不是您正在寻找的答案,您可以随时将 children 导入新组件,然后使用 shouldComponentUpdate 那里