在我的公司,我们正在将Web应用程序的前端迁移到ReactJS . 我们正在使用create-react-app(更新到v16),没有Redux . 现在我停留在一个页面上,可以通过以下图像简化结构:
在MainContainer的 componentDidMount()
方法中,使用相同的后端请求检索由三个组件(SearchableList,SelectableList和Map)显示的数据 . 然后,此请求的结果将存储在MainContainer的状态中,并且具有或多或少的结构:
state.allData = {
left: {
data: [ ... ]
},
right: {
data: [ ... ],
pins: [ ... ]
}
}
LeftContainer从MainContainer接收为 state.allData.left
并将 props.left.data
传递给SearchableList,再次作为prop .
RightContainer从MainContainer接收为 state.allData.right
并将 props.right.data
传递给SelectableList,将 props.right.pins
传递给Map .
SelectableList显示一个复选框以允许对其项目执行操作 . 每当在SelectableList组件的项目上发生动作时,它可能对Map引脚有副作用 .
我决定在RightContainer状态下存储一个列表,该列表保留SelectableList显示的所有项目ID;此列表作为道具传递给SelectableList和Map . 然后我向SelectableList传递一个回调函数,无论何时进行选择都会更新RightContainer中的id列表;新道具到达SelectableList和Map,因此在两个组件中调用 render()
.
它工作正常并有助于保留RightContainer中可能发生的SelectableList和Map的所有内容,但我问这对于 lifting-state-up 和 single-source-of-truth 概念是否正确 .
作为可行的替代方案,我想到在MainContainer的 state.right.data
中为每个项添加一个 _selected
属性,并将select回调三个级别传递给SelectableList,处理MainContainer中的所有可能操作 . 但是一旦选择事件发生,这将最终强制加载LeftContainer和RightContainer,引入了实现像 shouldComponentUpdate()
这样的逻辑的需要,以避免无用 render()
,特别是在LeftContainer中 .
Which is / could be the best solution to optimise this page from an architectural and performance point of view?
下面是我的组件摘录,以帮助您了解情况 .
MainContainer.js
class MainContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
allData: {}
};
}
componentDidMount() {
fetch( ... )
.then((res) => {
this.setState({
allData: res
});
});
}
render() {
return (
<div className="main-container">
<LeftContainer left={state.allData.left} />
<RightContainer right={state.allData.right} />
</div>
);
}
}
export default MainContainer;
RightContainer.js
class RightContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedItems: [ ... ]
};
}
onDataSelection(e) {
const itemId = e.target.id;
// ... handle itemId and selectedItems ...
}
render() {
return (
<div className="main-container">
<SelectableList
data={props.right.data}
onDataSelection={e => this.onDataSelection(e)}
selectedItems={this.state.selectedItems}
/>
<Map
pins={props.right.pins}
selectedItems={this.state.selectedItems}
/>
</div>
);
}
}
export default RightContainer;
提前致谢!
3 回答
正如React docs所述
所以基本上你需要提升那些正在用尽兄弟姐妹组件的树状态 . 所以你首先实现将
selectedItems
存储为RightContainer
中的状态是完全合理的并且是一种好的方法,因为父亲不需要知道这个data
正由RightContainer
的两个child
组件共享有一个单一的事实来源 .根据你的问题:
我不同意这是比第一个更好的方法,因为你
MainContainer
不需要知道selectedItems
或处理任何更新 .MainContainer
没有对这些状态做任何事情,只是将其传递下去 .考虑到
optimise on performance
,您自己谈论实现shouldComponentUpdate
,但是您可以通过扩展React.PureComponent来创建组件来避免这种情况,React.PureComponent本质上通过state
和props
的shallow
比较来实现shouldComponentUpdate
.根据文件:
但是,如果多个深度嵌套的组件正在使用相同的数据,那么使用redux并将该数据存储在redux-state中是有意义的 . 通过这种方式,整个App可以全局访问,并且可以在不直接相关的组件之间共享 .
例如,考虑以下情况
现在,如果Home和MyComp都想访问相同的数据 . 您可以通过
render
prop调用数据作为道具从App传递 . 但是,通过使用connect
函数将这两个组件连接到Redux状态可以轻松完成和
MyComp
类似 . 此外,它还可以轻松配置更新相关信息的操作此外,它还特别容易为您的应用程序配置Redux,并且您可以在各个Reducer中存储与相同内容相关的数据 . 通过这种方式,您也可以将应用程序数据模块化
我对此有诚实的建议 . 从经验来看:
Redux很简单 . 它很容易理解和扩展但是你应该将Redux用于某些特定的用例 .
由于Redux封装了您的应用程序,您可以考虑存储以下内容:
当前应用语言环境
当前经过身份验证的用户
来自某个地方
当前令牌
你需要在全球范围内的东西 . react-redux甚至允许在组件上使用 @connect 装饰器 . 所以喜欢:
这些都作为道具传递下来,连接可以在应用程序的任何地方使用 . 虽然我建议只使用spread运算符传递全局道具
应用程序内的所有其他组件(或“页面”)都可以执行自己的封装状态 . 例如,“用户”页面可以自己做 .
您可以通过props访问locale和currentUser,因为它们是从Container组件传递下来的 .
这种方法我多次完成它并且它可以工作 .
But ,因为你想要真正整合React的知识,在做Redux之前,你可以将你的状态存储在顶级组件上并将其传递给子级 .
Downsides:
你必须不断将它们传递给内层组件
要从内层组件更新状态,您必须传递更新状态的函数 .
这些缺点有点无聊且管理起来很麻烦 . 这就是Redux建成的原因 .
希望我帮忙 . 祝好运
通过使用Redux,你可以避免这样的回调,并将整个状态保存在一个单独的商店中 - 所以让你的父组件连接组件 - 并使左右组件变得愚蠢 - 并且只是传递你从父母到孩子的道具 - 而你在这种情况下不必担心回调 .