Shan Robertson
Shan Robertson

Reputation: 2742

returning nested state with redux and thunk

I'm fairly new to redux & thunk, and have been following tutorials to try and understand, and am managing to work it into my app ok. One thing i'm not understanding, is how i can get several state objects on the root level into one nested object. For example, right now my state looks like:

{
  timeline: [Array] // My timeline data in an array of objects
  timelineHasErrored: false,
  timelineIsLoading: false
}

But what I really want is:

{
  timeline : {
    data: [Array] // My timeline data in an array of objects
    hasErrored: false,
    isLoading: false
  }
}

and i'm really not quite sure how to nest these, or what the proper way to do that is. Below is my redux code, it's pretty simple so i'll post it all.

Reducers index

import { combineReducers } from 'redux'
import { timeline, timelineHasErrored, timelineIsLoading } from './timeline'

export default combineReducers({
    timeline, timelineHasErrored, timelineIsLoading
});

Timeline Reducers

import { TIMELINE_HAS_ERRORED, TIMELINE_IS_LOADING, TIMELINE_FETCH_DATA_SUCCESS } from '../constants/action-types.js'

export function timelineHasErrored(state = false, action) {
  switch (action.type) {
    case TIMELINE_HAS_ERRORED:
      return action.hasErrored;
    default:
      return state;
  }
}

export function timelineIsLoading(state = false, action) {
  switch (action.type) {
    case TIMELINE_IS_LOADING:
      return action.isLoading;
    default:
      return state;
  }
}

export function timeline(state = [], action) {
  switch (action.type) {
    case TIMELINE_FETCH_DATA_SUCCESS:
      return action.timeline;
    default:
      return state;
  }
}

Actions

import { TIMELINE_HAS_ERRORED, TIMELINE_IS_LOADING, TIMELINE_FETCH_DATA_SUCCESS } from '../constants/action-types.js'
import api from '../services/api'

export function timelineHasErrored(bool) {
  return {
    type : TIMELINE_HAS_ERRORED,
    hasErrored : bool
  }
}

export function timelineIsLoading(bool) {
  return {
    type : TIMELINE_IS_LOADING,
    isLoading : bool
  }
}

export function timelineFetchDataSuccess(timeline) {
  return {
    type : TIMELINE_FETCH_DATA_SUCCESS,
    timeline
  }
}

export function timelineFetchData() {
  return dispatch => {
    dispatch( timelineIsLoading(true) )

    api.getTracks().then(
      res => {
        dispatch( timelineIsLoading(false) )
        dispatch( timelineFetchDataSuccess(res.body) )
      },
      err => {
        dispatch( timelineIsLoading(false) )
        dispatch( timelineHasErrored(true) )
      }
    )
  }
}

And then in my react component I format the object like how i want it... but i think it would be better to have it nested in the actual state so i'm not creating extra work for myself if things change

// Redux State
const mapStateToProps = (state) => {    
  const obj = {
    timeline : {
      data : state.timeline,
      hasErrored: state.tracksHasErrored,
      isLoading: state.tracksIsLoading
    }
  }

  return obj
}

// Redux Dispatch
const mapDispatchToProps = (dispatch) => {
  return {
    fetchData: () => dispatch( timelineFetchData() )
  }
}

If anybody has any tips or corrections for me bring em on, i'm trying to get a solid grasp on redux, thanks!

Upvotes: 0

Views: 367

Answers (1)

Anthony
Anthony

Reputation: 6482

Your timeline reducer is pretty small, so you could have it as a single reducer as follows:

const initialState = {
    data: [],
    hasErrored: false,
    isLoading: false
};

export function timeline(state = initialState, action) {
  switch (action.type) {
    case TIMELINE_HAS_ERRORED:
      return {
         ...state,
         hasErrored: action.hasErrored
      };

    case TIMELINE_IS_LOADING:
      return {
         ...state,
         isLoading: action.isLoading
      };

    case TIMELINE_FETCH_DATA_SUCCESS:
      return {
         ...state,
         data: action.timeline
      };

    default:
      return state;
  }
}

Then you wouldn't need to call combineReducers(), unless you had other reducers.

Upvotes: 3

Related Questions