lorenzoid
lorenzoid

Reputation: 1832

How to correctly update my initialState in react-redux

I have an initialState:

export default {
  dashboards: [],
  dashboardContent: []
};

Which I would like to update via the reducer after an action dispatch like so:

import initialState from './initialState';
import * as types from '../actions/actionTypes';

export default function dashboardContentReducer(state = initialState, action) {
  switch(action.type) {
    case types.LOAD_DASHBOARD_CONTENT_SUCCESS:
      return Object.assign({}, state, { dashboardContent: action.dashboardContent });
    default:
      return state;
  }
}

and

import initialState from './initialState';
import * as types from '../actions/actionTypes';

export default function dashboardReducer(state = initialState, action) {
  switch(action.type) {
    case types.LOAD_DASHBOARDS_SUCCESS:
      return Object.assign({}, state, { dashboards: action.dashboards });
    default:
      return state;
  }
}

After dispatching the actions, when I look into my store, I see that it has been updated, but one level too deep:

{
  dashboards: {
    dashboards: [/* array received from reducer */],
    dashboardContent: []
  },
  dashboardContent: {
    dashboards: [],
    dashboardContent: [/* array received from reducer */]
  }
}

How do I make it so that it turns into my intended which should simply be:

{
  dashboards: [/* payload received from reducer */],
  dashboardContent: [/* payload received from reducer */]
}

EDIT:

Here is how the payload looks.

For dashboardContent:

[
  {
    application: 'Company News',
    site: 'Home',
    lastUpdated: 'Jun 1, 2016 10:30',
    location: 'Asbury',
    views: 123451,
    individuals: 2345
  },
  {
    application: 'Company News',
    site: 'Home',
    lastUpdated: 'Jun 1, 2016 10:30',
    location: 'Asbury',
    views: 123451,
    individuals: 2345
  },
  {
    application: 'Company News',
    site: 'Home',
    lastUpdated: 'Jun 1, 2016 10:30',
    location: 'Asbury',
    views: 123451,
    individuals: 2345
  },
  {
    application: 'Company News',
    site: 'Home',
    lastUpdated: 'Jun 1, 2016 10:30',
    location: 'Asbury',
    views: 123451,
    individuals: 2345
  },
  {
    application: 'Company News',
    site: 'Home',
    lastUpdated: 'Jun 1, 2016 10:30',
    location: 'Asbury',
    views: 123451,
    individuals: 2345
  }
];

for dashboards:

[
  {
    id: 1,
    title: "Overview",
    template: "main",
    category: "Main",
    sort: {},
    filter: {
    loginType: "All Accounts",
    Pivot: "location",
    pivotValue: 1
    },
    priority: 0,
    readonly: true
  },
  {
    id: 2,
    title: "Top Applications",
    template: "application-usage",
    category: "Application Usage",
    sort: {
      first: {
        by: "views",
        direction: "desc"
      },
      second:  {
        By:"individuals",
        direction: "desc"
      }
    },
    filter: {
      application: 0,
      Pivot: location,
      pivotValue: 1
    },
    priority: 1,
    readonly: true,
    createdDate: "2016-12-29T16:37:11.62Z",
    updatedDate: "2016-12-29T16:37:11.62Z"
  }
]

Upvotes: 2

Views: 1679

Answers (3)

CodingIntrigue
CodingIntrigue

Reputation: 78535

From the combineReducers docs:

The resulting reducer calls every child reducer, and gathers their results into a single state object. The shape of the state object matches the keys of the passed reducers.

Consequently, the state object will look like this:

{
  reducer1: ...
  reducer2: ...
}

So when using it, in order for two reducers to share state, you need to create a single reducer like so:

import initialState from './initialState';
import * as types from '../actions/actionTypes';

export default function dashboardReducer(state = initialState, action) {
  switch(action.type) {
    case types.LOAD_DASHBOARDS_SUCCESS:
      return Object.assign({}, state, { dashboards: action.dashboards });
    case types.LOAD_DASHBOARD_CONTENT_SUCCESS:
      return Object.assign({}, state, { dashboardContent: action.dashboardContent });
    default:
      return state;
  }
}

Or if you want to keep the logic separate for whatever reason, you can use the reduce-reducers library:

import reduceReducers from "reduce-reducers";
import dashboardReducer from "./dashboardReducer";
import dashboardContentReducer from "./dashboardContentReducer";

const combined = reduceReducers(dashboardReducer, dashboardContentReducer);

createStore(combineReducers({ dashboard: combined }));

That way you will have a state object:

{
    dashboard: {
        dashboards: []
        dashboardContent: []
    }
}

Upvotes: 1

semuzaboi
semuzaboi

Reputation: 5172

Since you are using two different reducers, and you are aware of the initialState , why not modify your reducers as

import initialState from './initialState';
import * as types from '../actions/actionTypes';

export default function dashboardContent(state = [], action) {
  switch(action.type) {
    case types.LOAD_DASHBOARD_CONTENT_SUCCESS:
      return Object.assign({}, state, action.data);
    default:
      return state;
  }
}

and the other one as

import initialState from './initialState';
import * as types from '../actions/actionTypes';

export default function dashboard(state = [], action) {
  switch(action.type) {
    case types.LOAD_DASHBOARDS_SUCCESS:
      return Object.assign({}, state, action.data);
    default:
      return state;
  }
}

Well, i have changed the names of the reducers, but while usingcombineReducers the naming doesnt matter anyway. As you can always do.

combineReducers({
  dashboardContent:dashboardContentReducersWhichHasBeenGivenAWeirdName,
  dashboard:dashboardReducerAgainNamedWeird
})

Your state will be created as {dashboard:[],dashboardContent:[]}

By this way you can avoid the updates at the wrong level. also i'd recommened taking a look at the explanation here by Dan , it explains in detail how createStore creates the state based on your reducers, also how intialState takes precedence.

Upvotes: 2

Shubham Khatri
Shubham Khatri

Reputation: 281726

You need to target the inner dashboards and not the outer one. Try

export default function dashboardReducer(state = initialState, action) {
  switch(action.type) {
    case types.LOAD_DASHBOARDS_SUCCESS:
      return Object.assign({}, state, { 
                dashboards: Object.assign({},state.dashboards,{dashboards: action.dashboards}
             });
    default:
      return state;
  }
}

and

export default function dashboardContentReducer(state = initialState, action) {
  switch(action.type) {
    case types.LOAD_DASHBOARD_CONTENT_SUCCESS:
      return Object.assign({}, state, { 
                dashboards: Object.assign({},state.dashboards,{dashboardContent: action.dashboardContent}
             });
    default:
      return state;
  }
}

Upvotes: 0

Related Questions