代码之家  ›  专栏  ›  技术社区  ›  jdowdell

如何将redux存储中的封装ui状态映射到带有react-redux的props?

  •  3
  • jdowdell  · 技术社区  · 10 年前

    我很难理解如何组合ui小部件减速器和串联反应渲染树,这样我可以稍后使用反应redux将减速器的结果redux存储映射到渲染树中的props。

    假设有这样的设置。我正在尝试制作一个NameWidget,可以在任何应用程序的任何位置使用:

    NameView.jsx:
    ...
    render() {
      return <div> Name: {this.props.name} </div>;
    }
    ...
    

    ====

    NameAction.js:
    ...
    export const CHANGE_NAME = 'CHANGE_NAME';
    ...
    export function changeNameAction(newName) {
      return {
        type: CHANGE_NAME,
        payload: newName,
      };
    }
    

    ====

    NameReducer.js:
    
    import { CHANGE_NAME } from './NameAction';
    
    function ui(state = {name: ''}, action) {
      switch(action.type) {
        case(CHANGE_NAME):
          return Object.assign({}, state, {name: action.payload});
          break;
        default:
          return state;
      }
    }
    
    export default ui;
    

    ====

    NameWidget.js:
    
    import { connect } from 'react-redux';
    import { NameView } from './NameView';
    
    const mapStateToProps = (state) => {
      return {
        name: state.name,
      };
    };
    
    const NameWidget = connect(
      mapStateToProps
    )(NameView);
    
    export default NameWidget;
    

    如果我直接使用NameWidget,我不会有问题:

    NameApp.jsx:
    
    import { createStore } from 'redux';
    import app from './NameReducers';
    let store = createStore(app);
    ...
    function render() {
      return (
        <Provider store={store}>
          <NameWidget/>
        </Provider>
      );
    }
    

    然而,我不知道如何使这个模块化。我真的不能把这个Widget放到其他任何东西中。假设我想这样做:

    EncapsulatingView.jsx:
    ...
    render() {
      return (
        <div>
          <NameWidget/>
          <SomeOtherReduxWidget/>
        </div>
      );
    }
    ...
    

    ====

    EncapsulatingReducer.js:
    import { combineReducers } from 'redux'
    import nameWidget from '../name/NameReducer';
    import someOtherWidget from '../someOther/SomeOtherReduxWidgetReducer';
    
    export default combineReducers({nameWidget, someOtherWidget});
    

    只要所有封装视图中的所有操作都具有唯一的类型,就可以正常工作。但问题是NameWidget的mapStateToProps;它需要从上面进行更改,以使用一个新路径来访问其店铺部分:

    ...
    const mapStateToProps = (state) => {
      return {
        name: state.nameWidget.name,
      };
    };
    ...
    

    等等,取决于祖先如何结合Reducers()来定义他们更强大的超级小部件和应用程序,后代必须改变他们映射StateToProps()的方式来补偿。这破坏了代码的封装性和可重用性,我不知道如何在react-redux中绕过它。如何通过组合combineReducers()来定义小部件,然后将结果存储映射到这些小部件的道具,以一次写入即可在任何地方使用的方式?

    (毫无疑问,重复的combineReducers()创建一个自下而上的形状似乎很奇怪,但mapStateToProps()似乎采用了自上而下的“绝对路径”方法来查看该形状。)

    2 回复  |  直到 10 年前
        1
  •  1
  •   John    10 年前

    有趣的问题。我想知道,要求父级生成一个guid并通过props向下传递是否符合您的目的,然后您只需将其用作小部件实例的状态对象的索引。因此,每当您要创建这个小部件的新实例时,您需要在父级中为它创建一个ID,然后将其传递到props中,然后它将对 connect 方法在mapStateToProps和mapDispatchToProps中使用。我想从理论上讲,您甚至可以自己创建这个父组件,它在使用时可能是透明的,只需使用componentDidMount生成一个新的guid来存储在组件状态中并传递给子组件即可。

        2
  •  0
  •   jdowdell    10 年前

    我认为John回答了这个问题,因为他基本上提供了正确的答案,通过mapStateToProps()的ownProps函数arg传递存储“路由”逻辑。但我更喜欢自己的旋转。基本上,每当您在EncapsulatingReducer中组合Reducers()时。js,您必须记住在EncapsulatingView.js中传递uiStorePath属性:

    New NameWidget.js:
    
    const _ = require('lodash');
    import { connect } from 'react-redux';
    import { NameView } from './NameView';
    
    const mapStateToProps = (state, props) => {
      const uiStore = (
        (ownProps && _.has(ownProps, 'uiStorePath') && ownProps.uiStorePath &&
            ownProps.uiStorePath.length) ? 
          _.get(state, ownProps.uiStorePath) :  // deep get...old versions of lodash would _.deepGet()
          state
      );
    
      return {
        name: uiStore.name,
      };
    };
    
    const NameWidget = connect(
      mapStateToProps
    )(NameView);
    
    export default NameWidget;
    

    ====

    EncapsulatingView.js:
    
    ...
    render() {
      const uiStorePathBase = ((this.props.uiStorePath &&         
          this.props.uiStorePath.length) ?
        this.props.uiStorePath + '.' :
        ''
      );
      return (
        <div> 
          <NameWidget uiStorePath={uiStorePathBase+"nameWidget"}/>
          <SomeOtherWidget uiStorePath={uiStorePathBase+"someOtherWidget"/>
        </div>
      );
    }
    ...