menu

React

A community of developers, designers and others who love React.js. ⚛️

Channels
Team

How do I combine reducers while managing state with useReducer hook + Context?

October 15, 2019 at 10:33pm

How do I combine reducers while managing state with useReducer hook + Context?

October 15, 2019 at 10:33pm (Edited 1 year ago)
I am building an application with a somewhat complex state. So I decided to use React UseReducer hook with Context API to manage my state Here are my individual reducers:
UserReducer:
import { actionTypes } from '../actions/user';
const userInitialState = {
navItems: []
};
const userReducer = (state = userInitialState, action = '') => {
switch (action.type) {
case actionTypes.CHANGE_NAVITEMS:
return {
...state,
navItems: action.data
};
default:
return state;
}
};
export default userReducer;
Nav Reducer:
import { actionTypes } from '../actions/nav';
const navInitialState = {
isLoaded: false,
submenuStates: []
};
const navReducer = (state = navInitialState, action = '') => {
switch (action.type) {
case actionTypes.CHANGE_SUBMENUSTATES:
return {
...state,
submenuStates: action.data
};
case actionTypes.CHANGE_ISLOADED:
return {
...state,
isLoaded: action.data
};
default:
return state;
}
};
export default navReducer;
Spinner Reducer:
import { actionTypes } from '../actions/spinner';
const spinnerInitialState = {
isLoading: false
};
const spinnerReducer = (state = spinnerInitialState, action = '') => {
switch (action.type) {
case actionTypes.CHANGE_ISLOADING:
return {
...state.nav,
isLoading: action.data
};
default:
return state;
}
};
export default spinnerReducer;
Here is my current attempt to combine reducers:
import navReducer from './nav';
import spinnerReducer from './spinner';
import userReducer from './user';
const reducers = ({ data, ui } = {}, action) => {
if (!data || !ui) {
return {
data: {
user: userReducer()
},
ui: {
nav: navReducer(),
spinner: spinnerReducer()
}
};
}
return {
data: {
user: userReducer(data.user, action)
},
ui: {
nav: navReducer(ui.nav, action),
spinner: spinnerReducer(ui.spinner, action)
}
};
};
const initialState = reducers();
export { reducers, initialState };
As you can probably tell, I was having a "can't set the property user of undefined error from trying the line
user: userReducer(data.user, action)
So I resorted to adding an if statement above. Basically, if data or ui are undefined, this code first calls the individual reducer functions with no parameters so they fall back to the defaults and that way they create an empty state object.
I find this pattern very ugly and repetitive. Is there a cleaner way of doing this where I'm not repeating myself?

October 16, 2019 at 2:42am
in most cases, i have very little state in and particular context instead, i have tons of contexts, and tons of hooks/consumers
  • reply
  • like
so, i would recommend not combining reducers, but having a context for each one
like-fill
1
  • reply
  • like
useSpinner(), useNavigation(), useUser(), etc...
  • reply
  • like
and I read how-to-use-react-context-effectively on a regular basis
like-fill
1
  • reply
  • like

October 16, 2019 at 1:53pm
in most cases, i have very little state in and particular context instead, i have tons of contexts, and tons of hooks/consumers
So if i have like 10 state data objects to maintain, I need to create 10 different contexts? That doesn't seem ideal.
  • reply
  • like
Everything has it's tradeoffs. If you want a single state object, then redux may be the way to go.
  • reply
  • like