Skip to content

Instantly share code, notes, and snippets.

@BlueWinds
Created July 23, 2018 16:10
Show Gist options
  • Select an option

  • Save BlueWinds/071635379914d31bce8319868b97709d to your computer and use it in GitHub Desktop.

Select an option

Save BlueWinds/071635379914d31bce8319868b97709d to your computer and use it in GitHub Desktop.
// In this version, each reducer is very clear - for example, looking at billingReducer,
// we know all the ways that users can get credit. If a user ends up with an invalid credit amount,
// we can easily start looking in the billingReducer to know all the places this could have come
// from.
// On the other hand, looking at VIEW_PERFORMER, it's not clear what exactly happens when the action is
// emitted. We have to go look at every reducer (or at least grep them) to know what the aciton is doing.
// Also note how 'VIEW_PERFORMER' needs extra context attached - it's not clear, looking at the action creator,
// why it needs purchaseHistory attached. What does that have to do with viewing a performer?
// Action creator
const viewPerformer = performer => (dispatch, getState) => {
dispatch({
type: 'VIEW_PERFORMER',
performer,
purchaseHistory: getState().purchaseHistory,
});
}
// Reducers
const performerReducer = (state, action) => {
switch (action.type) {
case 'VIEW_PERFORMER':
return action.performer;
default:
return state;
}
}
const locationReducer: (state, action) => {
switch (action.type) {
case 'VIEW_PERFORMER':
return {
...state,
location: `/cam/${action.performer.nickname}`,
};
default:
return state;
}
};
const billingReducer = (state, action) => {
switch (action.type) {
case 'VIEW_PERFORMER':
const recentlySpentOnPerf = action.purchaseHistory.some(purchase => purchase.performer === action.performer.id);
return {
...state,
discount: state.discount + recentlySpentOnPerf ? 500 : 0,
}
default:
return state;
}
};
// Root reducer
const rootReducer = (state, action) => {
return {
performer: performerReducer(state, action),
location: locationReducer(state, action),
billing: billingReducer(state, action),
purchaseHistory: purchaseHistoryReducer(state, action), // Not shown
}
};
// In this version, we know all the actions that happen when someone views a performer.
// The action creator function is explicit - it says exactly what happens, so there's no mystery.
// On the other hand, the reducers are far less informative. If I see that someone has discount,
// there's no clue why - I have to grep the codebase to find all the places that dispatch ADD_DISCOUNT.
// This can be especially tricky when debugging - if I see {type: 'ADD_DISCOUNT', amount: NaN}, that gives
// me to clues why it's NaN, or where to start looking for the bad action creator. The state is no longer well
// contained in just the reducers.
// Action creator
const viewPerformer = performer => (dispatch, getState) => {
dispatch({type: 'VIEW_PERFORMER', performer});
dispatch({type: 'GOTO', url: `/cam/${performer.nickname}`});
const recentlySpentOnPerf = getState().purchaseHistory.some(purchase => purchase.performer === performer.id);
if (recentlySpentOnPerf) {
dispatch({type: 'ADD_DISCOUNT', amount: 500});
}
}
// Reducers
const performerReducer = (state, action) => {
switch (action.type) {
case 'VIEW_PERFORMER':
return action.performer;
default:
return state;
}
}
const locationReducer: (state, action) => {
switch (action.type) {
case 'GOTO':
return {
...state,
url: action.url,
};
default:
return state;
}
};
const billingReducer = (state, action) => {
switch (action.type) {
case 'ADD_DISCOUNT':
return {
...state,
discount: state.discount + action.amount,
};
default:
return state;
}
}
// Root reducer
const rootReducer = (state, action) => {
return {
performer: performerReducer(state, action),
location: locationReducer(state, action),
billing: billingReducer(state, action),
}
}
// In this version, each action type has its own reducer. This makes them very clear, since I have one and only one place to look
// in order to figure out what happens during a VIEW_PERFOMER action, and that reducer has access to the entire state -
// no need to explicitly pass purchaseHistory in as part of the action.
// On the downside, there are no longer vertical slices of state - there's no easy way to see all the actions
// which modify a specific piece of of the store. It's difficult to find all the places that modify 'billing',
// since there isn't even a convenient string constant I can grep.
// Action creator
const viewPerformer = performerId => dispatch => {
dispatch({type: 'VIEW_PERFOMER', performer});
}
// Reducers
const viewPerformerReducer = (state, {performer}) => {
const recentlySpentOnPerf = state.purchaseHistory.some(purchase => purchase.performer === performer.id);
return {
performer,
billing: {
...state.billing,
credit: state.credit + recentlySpentOnPerf ? 500 : 0,
},
location: {
...state.location,
url: `/cam/${performer.nickname}`,
}
}
}
// Root reducer
const rootReducer = (state, action) => {
switch (action.type) {
case 'VIEW_PERFOMER':
return viewPerformerReducer(state, action);
default:
return state;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment