Skip to content

Instantly share code, notes, and snippets.

@alexgriff
Last active January 30, 2018 20:46
Show Gist options
  • Save alexgriff/81dacf490e5237fc96bb4c312aeda91f to your computer and use it in GitHub Desktop.
Save alexgriff/81dacf490e5237fc96bb4c312aeda91f to your computer and use it in GitHub Desktop.
// adadpted from an example in Fullstack React https://www.fullstackreact.com/
// -------------------------------------------------
// Original Code, Deeply Nested, Reducer is Complex
// -------------------------------------------------
// State contains an array of subjects and the id of the currently
// active subject. Each subject has many notes.
// Initially, before refactoring, the actions of adding / deleting a note are pretty messy
// because the state is so nested
// reducers/index.js
const initialState = [
{
id: 1,
title: 'React',
notes: []
},
{
id: 2,
title: 'Redux',
notes: []
},
{
id: 3,
title: 'Functional Programming',
notes: []
},
{
id: 4,
title: 'Ruby',
notes: []
}
];
const reducer = (state = initialState, action) => {
let subjectIndex;
let newSubjectNotes;
switch (action.type) {
case 'ADD_NOTE':
subjectIndex = state.subjects.findIndex(s => s.id === action.subjectId);
newSubjectNotes = [...state.subjects[subjectIndex].notes, action.note];
return {
...state,
subjects: [
...state.subjects.slice(0, subjectIndex),
{ ...state.subjects[subjectIndex], notes: newSubjectNotes },
...state.subjects.slice(subjectIndex + 1)
]
};
case 'DELETE_NOTE':
subjectIndex = state.subjects.findIndex(s => s.id === action.subjectId);
newSubjectNotes = state.subjects[subjectIndex].notes.filter(
subj => subj.timestamp !== action.id
);
return {
...state,
subjects: [
...state.subjects.slice(0, subjectIndex),
{ ...state.subjects[subjectIndex], notes: newSubjectNotes },
...state.subjects.slice(subjectIndex + 1)
]
};
case 'CHANGE_ACTIVE_SUBJECT':
return {
...state,
activeSubjectId: action.activeId
};
default:
return state;
}
};
// --------------------------------------------------------------------------------
// Refactored Code, uses combineReducers, calls one reducer function inside another
// --------------------------------------------------------------------------------
// no reducer will have deeply nested state
// -----------------
// reducers/index.js
import { combineReducers } from 'redux';
import subjects from './subjectsReducer';
import activeSubjectId from './activeSubjectIdReducer';
const rootReducer = combineReducers({
subjects,
activeSubjectId
});
export default rootReducer;
// ----------------------------------
// reducers/activeSubjectIdReducer.js
const initialState = 1;
export default activeSubjectIdReducer = (state = initialState, action) => {
switch (action.type) {
case 'CHANGE_ACTIVE_SUBJECT':
return action.activeId;
default:
return state;
}
};
// ---------------------------
// reducers/subjectsReducer.js
import notesReducer from './notesReducer';
const initialState = [
{
id: 1,
title: 'React',
notes: []
},
{
id: 2,
title: 'Redux',
notes: []
},
{
id: 3,
title: 'Functional Programming',
notes: []
},
{
id: 4,
title: 'Ruby',
notes: []
}
];
export default subjectsReducer = (state = initialState, action) => {
switch (action.type) {
case 'ADD_NOTE':
case 'DELETE_NOTE':
const subjectIndex = state.findIndex(s => s.id === action.subjectId);
const newSubjectNotes = notesReducer(state[subjectIndex].notes, action);
return [
...state.slice(0, subjectIndex),
{ ...state[subjectIndex], notes: newSubjectNotes },
...state.slice(subjectIndex + 1)
];
default:
return state;
}
};
// -------------------
// src/notesReducer.js
export default notesReducer = (state = [], action) => {
switch (action.type) {
case 'ADD_NOTE':
return [...state, action.note];
case 'DELETE_NOTE':
return state.filter(note => note.timestamp !== action.id);
default:
return state;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment