繁体   English   中英

链接connect / mapStateToProps / mapDispatchToProps函数以在react-redux中重用代码

[英]Chain connect/mapStateToProps/mapDispatchToProps functions for code reuse in react-redux

假设我有两个Redux连接的组件。 第一个是简单的待办事项加载/显示容器,其中以下函数传递给connect() mapStateToProps从redux状态读取mapDispatchToProps ,而mapDispatchToProps用于从服务器请求向该状态提供最新mapDispatchToProps列表:

TodoWidgetContainer.js

import TodoWidgetDisplayComponent from '...'

function mapStateToProps(state) {
  return {
    todos: todoSelectors.getTodos(state)
  };
}

function mapDispatchToProps(dispatch) {
  return {
    refreshTodos: () => dispatch(todoActions.refreshTodos())
  };
}

connect(mapStateToProps, mapDispatchTo)(TodoWidgetDisplayComponent);

第二个Redux组件旨在应用于页面上的任何组件,以便该组件可以指示是否显示全局“正在加载”图标。 由于可以在任何地方使用它,因此我创建了一个辅助函数,该函数将MapDispatchToProps包装在一个闭包中,并为每个组件生成一个ID,该ID用于确保请求加载程序的所有组件都指示它们不再需要它,并且全局加载器可以隐藏。

这些函数基本上如下,其中mapStateToProps将加载程序的可见性暴露给组件,而mapDispatchToProps允许它们请求加载程序显示或隐藏。

Loadify.js

function mapStateToProps(state) {
  return {
    openLoader: loaderSelectors.getLoaderState(state)
  };
}

function mapDispatchToProps() {
  const uniqId = v4();
  return function(dispatch) {
    return {
      showLoader: () => {
        dispatch(loaderActions.showLoader(uniqId));
      },
      hideLoader: () => {
        dispatch(loaderActions.hideLoader(uniqId));
      }
    };
  };
}

export default function Loadify(component) {
  return connect(mapStateToProps, mapDispatchToProps())(component);
}

因此,现在,如果我有一个要允许访问加载程序的组件,则可以执行以下操作:

import Loadify from '...'

class DisplayComponent = new React.Component { ... }

export default Loadify(DisplayComponent);

并且它应该给它一个唯一的ID,允许它请求加载器显示/隐藏,并且只要有一个组件要求显示/隐藏,加载器图标就会显示。 到目前为止,这一切似乎都工作正常。

我的问题是,如果我想将其应用于todos组件,以便该组件既可以请求/接收其todo,又可以在处理过程中请求加载器显示,我是否可以做以下事情:

TodoWidgetContainer.js

import Loadify from '...'
import TodoWidgetDisplayComponent from '...'

function mapStateToProps(state) {
  return {
    todos: todoSelectors.getTodos(state)
  };
}

function mapDispatchToProps(dispatch) {
  return {
    refreshTodos: () => dispatch(todoActions.refreshTodos())
  };
}

const TodoContainer = connect(mapStateToProps, mapDispatchTo)(TodoWidgetDisplayComponent);

export default Loadify(TodoContainer);

假设没有重复的键,redux是否会自动将对象合并在一起以使其兼容? 还是只需要最新的mapStateToProps / mapDispatchTo集,除非我进行某种手动合并? 还是有一种更好的方法来获得我没有看到的这种可重用性? 我确实希望避免为我们需要的每个组件创建一组自定义的容器。

好吧,这就是我要做的。 创建一个高阶组件(HOC),该组件将新的微调器引用添加到减速器。 HOC将通过绑定生命周期方法来初始化和销毁​​redux中对微调器的引用。 HOC将为基础组件提供两个属性。 第一个是isLoading ,它是一个带有布尔参数的函数。 true打开,false关闭。 第二个属性是spinnerState ,它是spinnerState当前状态的只读布尔值。

我创建的这个示例没有动作创建者或还原器,请告诉我是否需要它们的示例。

loadify.jsx

/*----------  Vendor Imports  ----------*/
import React from 'react';
import { connect } from 'react-redux';
import v4 from 'uuid/v4';

/*----------  Action Creators  ----------*/
import {
  initNewSpinner,
  unloadSpinner,
  toggleSpinnerState,
} from '@/wherever/your/actions/are'


const loadify = (Component) => {
  class Loadify extends React.Component {

    constructor(props) {
      super(props);
      this.uniqueId = v4();
      props.initNewSpinner(this.uniqueId);;
      this.isLoading = this.isLoading.bind(this);
    }

    componentWillMount() {
      this.props.unloadSpinner(this.uniqueId);
    }

    // true is loading, false is not loading
    isLoading(isOnBoolean) {
      this.props.toggleSpinner(this.uniqueId, isOnBoolean);
    }

    render() {
      // spinners is an object with the uuid as it's key
      // the value to the key is weather or not the spinner is on.
      const { spinners } = this.props;
      const spinnerState = spinners[this.uniqueId];
      return (
        <Component isLoading={this.isLoading} spinnerState={spinnerState}  />
      );
    }

  }

  const mapStateTopProps = state => ({
    spinners: state.ui.spinners,
  });

  const mapDispatchToProps = dispatch => ({
    initNewSpinner: uuid => dispatch(initNewSpinner(uuid)),
    unloadSpinner: uuid => dispatch(unloadSpinner(uuid)),
    toggleSpinner: (uuid, isOn) => dispatch(toggleSpinnerState(uuid, isOn))
  })

  return connect(mapStateTopProps, mapDispatchToProps)(Loadify);

};

export default loadify;

用例示例

import loadify from '@/location/loadify';
import Spinner from '@/location/SpinnerComponent';

class Todo extends Component {

  componentWillMount() {
    this.props.isLoading(true);
    asyncCall.then(response => {
      // process response
      this.props.isLoading(false);
    })
  }

  render() {
    const { spinnerState } = this.props;
    return (
      <div>
        <h1>Spinner Testing Component</h1>
        { spinnerState && <Spinner /> }
      </div>
    );
  }

}

// Use whatever state you need
const mapStateToProps = state => ({
  whatever: state.whatever.youneed,
});

// use whatever dispatch you need
const mapDispatchToProps = dispatch => ({
  doAthing: () => dispatch(doAthing()),
});

// Export enhanced Todo Component
export default loadify(connect(mapStateToProps, mapDispatchToProps)(Todo));

connect将自动将“传递给包装器组件的mapState ”,“来自此组件的mapDispatchmapState ”和“来自此组件的mapDispatchmapDispatch ”的组合合并在一起。 该逻辑的默认实现很简单:

export function defaultMergeProps(stateProps, dispatchProps, ownProps) {
  return { ...ownProps, ...stateProps, ...dispatchProps }
}

因此,如果您在彼此之间堆叠多级connect ,则被包装的组件将接收所有这些道具,只要它们的名称不同即可。 如果这些道具中的任何一个确实具有相同的名称,则根据此逻辑,只有一个道具会出现。

暂无
暂无

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

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