Reputation: 1832
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
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
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
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