React Redux——我可以让mapStateToProps只接受部分状态吗?
React Redux -- can I make mapStateToProps only take in part of the state?
我想制作可重用的模块,可以插入到任何react-redux应用程序中。理想情况下,我的模块将在顶层有一个容器组件、操作和reducer(然后是容器下面的任何表示组件)。我希望模块只工作在它自己的应用程序的状态片,理想情况下,不需要知道任何关于应用程序的状态的其余部分(所以它是真正的模块化)。
Reducers只在部分状态下工作(使用combinerreducers),所以我很高兴。然而,对于容器组件,mapStateToProps似乎总是接受应用程序的完整状态。
我想如果mapStateToProps只采取了相同的"状态片",我在我的模块处理(像减速器)。这样我的模块才是真正的模块。这可能吗?我想我可以把状态的那个切片传递给这个组件的道具(所以我可以使用mapStateToProps的第二个参数,ownProps),但我不确定这是否会有同样的效果。
这其实是一个比较复杂的话题。由于Redux是一个单一的全局存储,因此完全封装、完全可重用的即插即用逻辑集的想法确实变得相当困难。特别是,虽然减速器逻辑可以相当通用并且不知道它在哪里,但选择器函数需要知道在树的哪里可以找到该数据。
你的问题的具体答案是"不,mapState
总是给定完整的状态树"。
我有一些相关资源的链接,可能对你的情况有帮助:
- 有几个现有的库试图在Redux中实现"每个组件状态"。我在我的Redux插件目录中有一个列表,在组件状态类别。一组开发人员一直在讨论和原型化各种方法来实现"Redux中的可重用逻辑模块"的概念。他们的工作在https://github.com/slorber/scalable-frontend-with-elm-or-redux。Randy Coulman最近发表了一篇关于Redux中状态封装和模块化的三部分系列博客。他没有给出明确的答案,但这些帖子值得一读:封装Redux状态树,Redux减速器不对称,模块化减速器和选择器。
尽管mapStateToProps
(传递给连接的第一个函数)像您所说的那样传递了整个存储,但它的工作是将状态的特定部分映射到组件。所以只有从mapStateToProps返回的内容才会作为道具映射到你的组件。
假设你的状态是这样的:
{
account: {
username: "Jane Doe",
email: "janedoe@somemail.com",
password: "12345",
....
},
someOtherStuff: {
foo: 'bar',
foo2: 'bar2'
},
yetMoreStuff: {
usuless: true,
notNeeded: true
}
}
和你的组件需要从someOtherStuff
的account
和foo
的一切,然后你的mapStateToProps
看起来像这样:
const mapStateToProps = ({ account, someOtherStuff }) => ({
account,
foo: { someOtherStuff }
});
export default connect(mapStateToProps)(ComponentName)
那么您的组件将具有从您的redux状态映射的prop account
和foo
。
Redux只有一个存储,所以它所知道要做的就是将整个存储传递给mapStateToProps函数。但是,使用对象解构,您可以指定想要的存储中的哪些属性,而忽略其他属性。像‘function mapStateToProps({prop1, prop2})’这样的语句只会捕获存储中的这两个属性,而忽略其他属性。您的函数仍然接收整个存储,但您表明只有这些道具您感兴趣。
在我的例子中,'prop1'和'prop2'将是您在调用'combineReducers'时分配给reducers的名称。
理想情况下,它的工作方式是您获得状态,并使用解构器从中提取值。Redux工作在单状态的概念
例如: -
function mapStateToProps(state){
const { auth } = state //just taking a auth as example.
return{
auth
}
}
我遇到了同样的问题,因为,正如你所说的,当前redux/react-redux的实现允许在状态上拆分reducer,但mapDispatchToProps 总是传递整个状态树。
https://stackoverflow.com/a/39757853/444794不是我想要的,因为它意味着我们必须在每个使用我们模块的react-redux应用程序中复制我们所有的选择器逻辑。
我目前的解决方法是将状态的切片作为prop传递下去。这遵循了一种组合模式,但同时消除了直接访问状态的清洁度,这让我感到失望。
的例子:
一般来说,你想这样做:
const mapStateToProps = (state) => {
return {
items: mySelector(state)
}
}
const mapDispatchToProps = (dispatch) => {
return {
doStuff: (item) => {
dispatch(doStuff(item))
}
}
}
class ModularComponent extends React.Component {
render() {
return (
<div>
{ this.props.items.map((item) => {
<h1 onclick={ () => this.props.doStuff(item) }>{item.title}</h1>
})}
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ModularComponent)
,但是由于这个模块包含在一个应用程序中,其中的状态现在是几个东西(即。键值)而不是项列表,这将不起作用。我的解决方案看起来像:
const mapStateToProps = (_, ownProps) => {
return {
items: mySelector(ownProps.items)
}
}
const mapDispatchToProps = (dispatch) => {
return {
doStuff: (item) => {
dispatch(doStuff(item))
}
}
}
class ModularComponent extends React.Component {
render() {
return (
<div>
{ this.props.items.map((item) => {
<h1 onclick={ () => this.props.doStuff(item) }>{item.title}</h1>
})}
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ModularComponent)
使用Module的应用程序看起来像:
const mapStateToProps = (state) => {
return {
items: state.items
stuffForAnotherModule: state.otherStuff
}
}
class Application extends React.Component {
render() {
return (
<div>
<ModularComponent items={ this.props.items } />
<OtherComponent stuff={ this.props.stuffForAnotherModule } />
</div>
)
}
}
export default connect(mapStateToProps)(Application)
您可以选择为您的模块编写几个包装工具,这些工具将完成以下工作:1)仅在模块的状态切片更改时运行mapStateToProps, 2)仅将模块的切片传递给mapStateToProps。
这些都假设你的模块状态切片是应用状态对象的根属性(例如state.module1
, state.module2
)。
- 自定义
areStatesEqual
包装器函数,确保mapStateToProps
只在模块的子状态改变时运行:
function areSubstatesEqual(substateName) {
return function areSubstatesEqual(next, prev) {
return next[substateName] === prev[substateName];
};
}
然后传递给connect
:
connect(mapStateToProps, mapConnectToProps, null, {
areStatesEqual: areSubstatesEqual('myModuleSubstateName')
})(MyModuleComponent);
- 自定义
mapStateToProps
包装器,只在模块子状态中传递:
function mapSubstateToProps(substateName, mapStateToProps) {
var numArgs = mapStateToProps.length;
if (numArgs !== 1) {
return function(state, ownProps) {
return mapStateToProps(state[substateName], ownProps);
};
}
return function(state) {
return mapStateToProps(state[substateName]);
};
}
你可以这样使用:
function myComponentMapStateToProps(state) {
// Transform state
return props;
}
var mapSubstate = mapSubstateToProps('myModuleSubstateName', myComponentMapStateToProps);
connect(mapSubstate, mapDispatchToState, null, {
areStatesEqual: areSubstatesEqual('myModuleSubstateName')
})(MyModuleComponent);
当未测试时,最后一个例子应该只在'myModuleSubstateName'状态改变时运行myComponentMapStateToProps
,并且它只会接收模块的子状态。
一个额外的增强可以是编写自己的基于模块的connect
函数,该函数接受一个额外的moduleName
参数:
function moduleConnect(moduleName, mapStateToProps, mapDispatchToProps, mergeProps, options) {
var _mapState = mapSubstateToProps(moduleName, mapStateToProps);
var _options = Object.assign({}, options, {
areStatesEqual: areSubstatesEqual('myModuleSubstateName')
});
return connect(_mapState, mapDispatchToProps, mergeProps, _options);
}
然后每个模块组件只需要做:
moduleConnect('myModuleName', myMapStateToProps)(MyModuleComponent);
你的问题的答案是肯定的。两种答案涵盖了同一事物的不同方面。首先,Redux用多个reducer创建一个存储。所以你需要像这样组合它们:
export default combineReducers({
people: peopleReducer,
departments: departmentsReducer,
auth: authenticationReducer
});
然后,假设你有一个DepartmentsList组件,你可能只需要将departments
从商店映射到你的组件(也许一些动作也映射到道具):
function mapStateToProps(state) {
return { departments: state.departments.departmentsList };
}
export default connect(mapStateToProps, { fetchDepartments: fetchDepartments })(DepartmentsListComponent);
然后在你的组件里面基本上是:
this.props.departments
this.props.fetchDepartments()
- 事件和状态
- 获取选择框的状态
- 相位器状态未捕获参考错误
- 如何更改reactjs中外部/独立组件的状态或属性
- 如何使用密码检测网络中的状态连接
- Ember.js-接口状态应该存储在哪里
- 混合 ui-sref 和 $state.go 在 Angular ui-router 中进行状态转换
- 在Angular 2中布线期间保持零部件处于活动状态
- 在mvc应用程序中,在回发时保留最初隐藏的文本框的隐藏或可见状态
- XMLHttpRequest未返回值-状态202
- 使用javascript反复检查用户在facebook上的登录状态
- 如何使bxslider仅在移动视图中处于活动状态
- 获取ASP.NET Ajax Timer状态
- React redux初始化功能,无论状态变化如何
- 无法在现有状态转换期间更新-未使用任何非法的setState()
- 悬停下拉菜单即使在鼠标移出后也保持活动状态
- 从组件状态的数组中删除元素
- iOS 中的按钮触摸状态
- 使用AngularJS中的UI路由器将状态重定向到默认子状态
- 获取受 .slideToggle() 影响的显示状态元素