繁体   English   中英

React Redux - 我可以让mapStateToProps只占用部分状态吗?

[英]React Redux — can I make mapStateToProps only take in part of the state?

我想制作可以插入任何react-redux应用程序的可重用模块。 理想情况下,我的模块在顶层有一个容器组件,操作和reducer(然后是容器下面的任何表示组件)。 我希望模块只能处理自己应用程序状态的一部分,理想情况下不必了解应用程序状态的其余部分(因此它是真正的模块化)。

Reducers只能在部分状态下工作(使用combineReducers),所以我很高兴。 但是,对于容器组件,mapStateToProps似乎总是处于应用程序的完整状态。

如果mapStateToProps只接受我在模块中处理的相同“状态切片”(就像reducer一样),我会喜欢它。 这样我的模块就会真正模块化。 这可能吗? 我想我可以把状态的那一部分传递给这个组件的道具(所以我可以使用mapStateToProps的第二个参数,ownProps),但我不确定这是否具有相同的效果。

这实际上是一个复杂的话题。 因为Redux是一个单一的全局存储,所以完全封装,完全可重用的即插即用逻辑集的想法确实变得相当困难。 特别是,虽然reducer逻辑可以相当通用并且不知道它在哪里,但是选择器函数需要知道树中的哪个位置才能找到该数据。

你的问题的具体答案是“不, mapState总是给出完整的状态树”。

我确实有许多相关资源的链接,这可能对您的情况有所帮助:

如你所知,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的当前实现允许在状态上拆分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)

虽然mapStateToProps (你传递给connect的第一个函数)如你所说的那样传递整个商店,但它的工作是将状态的特定部分映射到组件。 因此,只有从mapStateToProps返回的内容才会被映射为组件的prop。

所以,让我们说你的州看起来像这样:

{
    account: {
        username: "Jane Doe",
        email: "janedoe@somemail.com",
        password: "12345",
        ....
    },
    someOtherStuff: {
        foo: 'bar',
        foo2: 'bar2'
    },
    yetMoreStuff: {
        usuless: true,
        notNeeded: true
    }
}

并且你的组件需要来自someOtherStuff accountfoo所有内容,那么你的mapStateToProps将如下所示:

const mapStateToProps = ({ account, someOtherStuff }) => ({
    account,
    foo: { someOtherStuff }
});
export default connect(mapStateToProps)(ComponentName)

然后你的组件将具有从你的redux状态映射的prop accountfoo

你可以选择为你的模块编写几个包装工具来完成以下工作:1)只有当模块的状态切片发生变化时才运行mapStateToProps,2)只将模块的切片传递给mapStateToProps。

这都假设您的模块状态切片是app状态对象上的根属性(例如state.module1state.module2 )。

  1. 自定义areStatesEqual包装函数,确保mapStateToProps仅在模块的子状态更改时才会运行:
function areSubstatesEqual(substateName) {
  return function areSubstatesEqual(next, prev) {
    return next[substateName] === prev[substateName];
  };
}

然后将其传递给connect

connect(mapStateToProps, mapConnectToProps, null, {
  areStatesEqual: areSubstatesEqual('myModuleSubstateName')
})(MyModuleComponent);
  1. 自定义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从商店映射到组件(也可能是映射到props的一些操作):

function mapStateToProps(state) {
  return { departments: state.departments.departmentsList };
}

export default connect(mapStateToProps, { fetchDepartments: fetchDepartments })(DepartmentsListComponent);

然后在组件内部基本上是:

this.props.departments
this.props.fetchDepartments()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM