Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct way to pre-load component data in react+redux

I do not know the correct way to pre-load data from API for a component to use.

I have written a stateless component which should render the data:

import React, { PropTypes } from 'react';

const DepartmentsList = ({ departments }) => {
  const listItems = departments.map((department) => (
    <li><a href="./{department}">{department.title}</a></li>
  ));
  return (
    <ul>
      {listItems}
    </ul>
  );
};

DepartmentsList.propTypes = {
  departments: PropTypes.array.isRequired
};

export default DepartmentsList;

And I have an action which will retreive data from the API:

import { getDepartments } from '../api/timetable';

export const REQUEST_DEPARTMENTS = 'REQUEST_DEPARTMENTS';
export const RECEIVE_DEPARTMENTS = 'RECEIVE_DEPARTMENTS';

const requestDepartments = () => ({ type: REQUEST_DEPARTMENTS });
const receiveDepartments = (departments) => ({ type: RECEIVE_DEPARTMENTS, departments });

export function fetchDepartments() {
  return dispatch => {
    dispatch(requestDepartments);
    getDepartments()
      .then(departments => dispatch(
        receiveDepartments(departments)
      ))
      .catch(console.log);
  };
}

Now I think I have a few options to preload departments that are required for the list. I could use redux-thunk and mapDispatchToProps to inject fetchDepartments to the stateless component and implement componentWillMount or similar lifecycle method, to load data - but then I don't need to pass the list via props, as the component would always load data for himself, and I don't want that, because whenever a new component is created the data is fetched from api instead of store...

Another advice I've seen is to use getComponent function from react-router, and retreive all data before returning the component, however, I am not sure if it's the correct redux way, as I don't see how to use redux-thunk there, and logic kind of seems littered all accross the files, when it's the data required for only one component.

This leaves me with the only seemingly ok option to load data in container component's lifecycle methods, but I want to know what is considered the best practice for what I want to do.

like image 259
Vincas Stonys Avatar asked Sep 06 '16 19:09

Vincas Stonys


1 Answers

The most 'redux-like' way of handling the pre-loading of data would be to fire off the asynchronous action in the lifecycle method (probably componentWillMount) of a Higher Order Component that wraps your app. However, you will not use the results of the API call directly in that component - it needs to be handled with a reducer that puts it into your app store. This will require you to use some sort of a thunk middleware to handle the asynchronous action. Then you will use mapStateToProps to simply pass it down to the component that renders the data.

Higher Order Component:

const mapStateToProps = (state) => {
  return {
    departments: state.departments
  };
}

const mapDispatchToProps = (dispatch) => {
  return bindActionCreators({
    getDepartments: actionCreators.fetchDepartments
  });
}

class App extends Component {
  componentWillMount() {
    this.props.getDepartments();
  }

  render() {
    return <DepartmentsList departments={this.props.departments} />
  }
}

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

reducers:

export function departments(state = [], action) {
  switch(action.type) {
    case 'RECEIVE_DEPARTMENTS':
      return action.departments;
  }
}
like image 56
Andy Noelker Avatar answered Sep 19 '22 19:09

Andy Noelker