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 中的非不可变字段。
有什么解决办法吗?
这里有不同的策略。
- 在您的子组件中使用
shouldComponentUpdate
(在这种情况下非常混乱和不雅);
- 将 Modal 组件连接到商店而不是
AppLayoutContainer
;
- 将
isModalShown
存储在Modal
的状态,而不是全局状态。
如果可能,最后的解决方案通常是更合适的恕我直言。您在相关组件中保持 UI 状态,因为您不需要经常在其他组件中意识到这一点。
class Modal extends Component {
state = {
isModalShown: false,
};
render() {
// ...
}
}
如果你需要在其他地方知道isModalShown
,你应该连接Modal
组件而不是AppLayoutContainer
。
export default connect((state) => ({
isModalShown: state.app.isModalShown,
}))(Modal)
您应该做的不是在 AppLayoutContainer
的 mapStateToProps
函数中传递 isModal
,而是 connect
Modal
本身。
mapStateToProps
更改的任何结果都将导致 connect
ed 组件重新呈现。在您的情况下,您的 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
那里
代码:
根组件
<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 中的非不可变字段。
有什么解决办法吗?
这里有不同的策略。
- 在您的子组件中使用
shouldComponentUpdate
(在这种情况下非常混乱和不雅); - 将 Modal 组件连接到商店而不是
AppLayoutContainer
; - 将
isModalShown
存储在Modal
的状态,而不是全局状态。
如果可能,最后的解决方案通常是更合适的恕我直言。您在相关组件中保持 UI 状态,因为您不需要经常在其他组件中意识到这一点。
class Modal extends Component {
state = {
isModalShown: false,
};
render() {
// ...
}
}
如果你需要在其他地方知道isModalShown
,你应该连接Modal
组件而不是AppLayoutContainer
。
export default connect((state) => ({
isModalShown: state.app.isModalShown,
}))(Modal)
您应该做的不是在 AppLayoutContainer
的 mapStateToProps
函数中传递 isModal
,而是 connect
Modal
本身。
mapStateToProps
更改的任何结果都将导致 connect
ed 组件重新呈现。在您的情况下,您的 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
那里