我应该使用ComponentDidMount还是connect函数的mergeProps进行数据提取?

时间:2019-02-19 07:55:18

标签: reactjs redux react-redux redux-thunk

我对redux使用react并有一个组件,该组件显示一些从外部源获取的dataSet。我当前的代码如下:

const ConnectedComponent = connect(
  state => ({
    dataSet: state.dataSet
  }),
  dispatch => ({
    loadData: () => {
      ...
      fetch data and dispatch it to the store
      ...
    }
  })
)(MyComponent); 

class MyComponent extends Component {
  ...
  componentDidMount() {
    const { dataSet, loadData } = this.props;
    if (!dataSet) {
      loadData();
    }
  } 
  ... 
  render () {
    const { dataSet } = this.props;
    if (dataSet) {
      // render grid with data
    } else {
      // render Loading...
    }
  }
}

上面的代码有效,但是我想知道摆脱 componentDidMount 并检查数据并从 connect 函数中加载数据会更好吗?该代码可能类似于:

const ConnectedComponent = connect(
  state => ({
    dataSet: state.dataSet
  }),
  dispatch => ({ 
    dispatch 
  }),
  (stateProps, dispatchProps) => {
    const { dataSet } = stateProps;
    const { dispatch } = dispatchProps;

    if (!dataSet) {
      // fetch data asynchronously and dispatch it to the store
    } 

    return {
      ...stateProps
    };
  }
)(MyComponent); 

class MyComponent extends Component {
  render () {
    const { dataSet } = this.props;
    if (dataSet) {
      // render grid with data
    } else {
      // render Loading...
    }
  }
}

由于 MyComponent 变得更简单,后一个代码对我来说更具吸引力。当 componentDidMount 检测到没有准备好显示的数据时,先执行从连接的组件到演示组件的传递,然后再执行的传递没有实现。

这种方法是否有缺点?

PS:我使用redux-thunk进行异步获取。

1 个答案:

答案 0 :(得分:2)

第二种方法,如概念分离,由于层和职责分离,可能是一个很好的解决方案-ConnectedComponent负责数据提取,而MyComponent充当演示组件。好!

但是,在连接mergeProps中调度动作似乎不是一个好主意,因为您会引入副作用

此外,我看到的另一个缺点是,获取和返回数据的流程将在您的不同页面(组件)之间重复。一般来说,将重复以下流程:

  1. 连接的组件为所需的实体调用API。
  2. 在获取实体时,我们正在显示加载程序。
  3. 当数据可用时,我们会将其传递给Presentational组件。

由于上述缺点,我建议您在 HOC 中组织和重用数据获取流程。

这是伪代码和流程(摘自我的文章),解决了上述缺点:

* 我过去一年一直在使用它,并继续坚持下去。

Fetcher HOC:

import authorActions from 'actions/author'

const actions = {
  'Author': authorActions
}

export default (WrappedComponent, entities) => {

  class Fetcher extends React.Component {
    // #1. Calls the API for the needed Entities.
    componentDidMount () {
      this.fetch()
    }

    fetch () {
      const { dispatch } = this.props

      entities.forEach(name => dispatch(actions[name].get()))
    }

    render () {
      const { isFetching } = this.props

      // #2. While fetching the Entities, we're showing an Loader.
      if (isFetching) return <Loader />

      return <WrappedComponent {...this.props} />
    }
  }

  const mapStateToProps = state => {
    const isFetching =  entities
      .map(entity => state[entity].isFetching)
      .filter(isFetching => isFetching)

    return { isFetching: isFetching.length > 0 }
  }

  return connect(mapStateToProps)(Fetcher)
}

用法:

const MyComponent = ({ authors }) => <AuthorsList authors={authors} />

const mapStateToProps = state => ({
  authors: state.authors
})

const Component = connect(mapStateToProps)(MyComponent)
export default Fetcher(Component, ['Author'])

在这里您可以阅读本文并深入了解其思想和概念:

* Fetcher概念在第2课:类固醇容器

Long-term React & Redux SPA — Lessons learned