我想制作可以插入任何react-redux应用程序的可重用模块。理想情况下,我的模块在顶层有一个容器组件,操作和reducer(然后是容器下面的任何表示组件)。我希望模块只能处理自己应用程序状态的一部分,理想情况是不必了解应用程序状态的其余部分(因此它真正模块化)。
Reducers只能在部分状态下工作(使用combineReducers),所以我很开心。但是,对于容器组件,mapStateToProps似乎总是处于应用程序的完整状态。
如果mapStateToProps只采用相同的"状态切片"我喜欢它。我在我的模块中处理(就像reducer一样)。这样我的模块就会真正模块化。这可能吗?我想我可以将该状态的片段传递给该组件的道具(所以我可以使用mapStateToProps的第二个参数,ownProps),但我不确定这是否会产生相同的效果。
答案 0 :(得分:10)
这实际上是一个复杂的话题。因为Redux是一个单一的全局存储,所以完全封装,完全可重用的即插即用逻辑集的想法确实变得相当困难。特别是,虽然reducer逻辑可以相当通用并且不知道它存在于何处,但是选择器函数需要知道树中的哪个位置才能找到该数据。
你问题的具体答案是“不,mapState
总是给出完整的状态树”。
我确实有许多相关资源的链接,这可能对您的情况有所帮助:
答案 1 :(得分:1)
如你所知,Redux只有一个商店,所以它知道要做的就是将整个商店传递给你的mapStateToProps函数。但是,使用对象解构,您可以指定所需的存储中的哪些属性,而忽略其余属性。像'function mapStateToProps({prop1,prop2})'这样的东西只能捕获商店中的那两个属性而忽略其余的属性。您的功能仍在接收整个商店,但您只是指出这些道具只对您感兴趣。
在我的示例中,'prop1'和'prop2'将是您在调用'combineReducers'期间为Reducer分配的名称。
答案 2 :(得分:0)
理想情况下,它的工作方式是获取状态,然后使用解构器从中提取值。 redux适用于单一状态的概念
例如: -
function mapStateToProps(state){
const { auth } = state //just taking a auth as example.
return{
auth
}
}
答案 3 :(得分:0)
我遇到了同样的问题,因为正如你所说,当前的redux / react-redux实现允许将状态上的reducers拆分得很好但是mapDispatchToProps 总是通过了整州树。
https://stackoverflow.com/a/39757853/444794不是我想要的,因为这意味着我们必须在使用我们模块的每个react-redux应用程序中复制所有选择器逻辑。
我目前的解决方法是将状态切片作为支柱传递下去。这遵循一种构成模式,但同时也消除了直接进入国家的清洁,这让我很失望。
示例:
通常,您希望这样做:
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)
使用该模块的应用程序如下:
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)
答案 4 :(得分:0)
虽然mapStateToProps
(您传递给连接的第一个函数)按照您的说法传递给整个商店,但它的工作是将状态的特定部分映射到组件。因此,只有从mapStateToProps返回的内容才会被映射为组件的道具。
让我们说你的州看起来像这样:
{
account: {
username: "Jane Doe",
email: "janedoe@somemail.com",
password: "12345",
....
},
someOtherStuff: {
foo: 'bar',
foo2: 'bar2'
},
yetMoreStuff: {
usuless: true,
notNeeded: true
}
}
并且您的组件需要来自account
的{{1}}和foo
的所有内容,然后您的someOtherStuff
将如下所示:
mapStateToProps
然后您的组件将从您的redux状态映射道具const mapStateToProps = ({ account, someOtherStuff }) => ({
account,
foo: { someOtherStuff }
});
export default connect(mapStateToProps)(ComponentName)
和account
。
答案 5 :(得分:0)
您确实可以选择为模块编写几个包装器utils,以完成以下工作:1)仅在模块的状态片更改时才运行mapStateToProps,2)仅将模块的片中的参数传递到mapStateToProps中。 / p>
所有这些都假设您的模块状态是应用程序状态对象的根属性(例如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);
答案 6 :(得分:-1)
你的问题的答案是肯定的。给出的答案都涵盖同一事物的不同方面。首先,Redux创建了一个包含多个reducer的商店。因此,您需要将它们组合起来:
export default combineReducers({
people: peopleReducer,
departments: departmentsReducer,
auth: authenticationReducer
});
然后,假设您有一个DepartmentsList组件,您可能只需要将departments
从商店映射到您的组件(也可能是映射到props的一些操作):
function mapStateToProps(state) {
return { departments: state.departments.departmentsList };
}
export default connect(mapStateToProps, { fetchDepartments: fetchDepartments })(DepartmentsListComponent);
然后在组件内部基本上是:
this.props.departments
this.props.fetchDepartments()