2
votes

I have a container component within my React and Redux application:

import { connect } from 'react-redux'

import MyComponent from '../components/mycomponent'

const mapStateToProps = state => ({
  myData: state.myData[state.activeDataId]
})

export default connect(mapStateToProps)(MyComponent)

If state.myData[state.activeDataId] does not exist then I want to dispatch an action to fetchMyData or fetchMyDataIfNeeded.

Note that, at the moment, my container does not contain any JSX, it just forwards props to a presentational component. I have seen this being called a 'Pure Container' though I'm not sure if that's a common term.

Is there a common pattern to dispatch actions from a Pure Container? I am thinking without:

  • expecting the presentational component to worry about this logic by passing an onLoad event to it
  • making the container a React.Component and triggering via componentDidMount

Is it a bad idea to dispatch actions from mapStateToProps, mapDispatchToProps or mergeProps?

3
It is a bad idea to dispatch actions in your container methods, it seems to be common practice either do it in the component* functions in your child component where needed as the connect HOC passes the dispatch function to your child component in this.props. - Purgatory

3 Answers

1
votes

As noted elsewhere, doing this in the container is a bad idea.

Instead of worrying about this in the container, it makes sense to fetch the data conditionally in your component. I know you mentioned not wanting to extend react.component, but you should definitely consider making this component a class in order to fetch data in a component lifecycle hook.

As detailed in another answer, connect takes a second argument of mapDispatchToProps. Pass in the fetchData dispatcher there (example of how to do this here.)

Then, in your component you can check myData. If it is not there, then you dispatch via

this.props.whatYouCalledDispatch()
1
votes

Yes, it is a bad idea to dispatch any action in container. In your case, the best approach is:

  • Map your state, action creator to component props
  • Check the props in componentDidMount (or componentDidUpdate) and fetchDataYouNeed, then component will be updated

Your container should be:

import { connect } from 'react-redux';
import {fetchDataYouNeed} from './actions
import MyComponent from '../components/mycomponent';

    const mapStateToProps = state => ({
      myData: state.myData[state.activeDataId]
    });

    const mapDispatchToProps = (dispatch) => {
      return {
        fetchDataYouNeed: ()=>{
          dispatch(fetchDataYouNeed());
        }
      };
    };

    export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);

Your component

class YourComponent extends Component{ 

  componentDidMount(){
    let {myData, activeDataId} = this.props;
    if(myData && !myData[activeDataId]){
      this.props.fetchDataYouNeed();
    }
  }

  render(){
    ....
  }
}

Learn more here https://facebook.github.io/react/docs/react-component.html#componentdidmount

0
votes

This seems to work, though I'm not sure if it has any unintended effects:

import { connect } from 'react-redux'

import MyComponent from '../components/mycomponent'
import { fetchMyData } from '../actions/mydata'

const mapStateToProps = state => ({
  dataId: state.activeDataId,
  myData: state.myData[state.activeDataId]
})

const mapDispatchToProps = { fetchMyData }

const mergeProps = (stateProps, dispatchProps) => {
  if (!stateProps.myData) {
    dispatchProps.fetchMyData(stateProps.dataId)
  }
  return stateProps
}
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(MyComponent)

Alternatively, brianzinn suggested that by using Redux Saga to manage side effects, this issue becomes redundant.