Okay, we're going to warm up by implementing a reducer in order to get everything up and running again.
We're going to implement the basic functionality.
const reducer = (state = [], action) => {
return state;
};And then we swap out that useState with a useReducer.
const [grudges, dispatch] = useReducer(reducer, []);We're going to create an action type and an action creator.
const ADD_GRUDGE = 'ADD_GRUDGE';
const FORGIVE_GRUDGE = 'FORGIVE_GRUDGE';const addGrudge = useCallback(
({ person, reason }) => {
dispatch({
type: ADD_GRUDGE,
payload: {
person,
reason
}
});
},
[dispatch]
);We'll add it to the reducer.
const reducer = (state = [], action) => {
if (action.type === ADD_GRUDGE) {
return [
{
id: uniqueId(),
...action.payload
},
...state
];
}
return state;
};Alright, pass it to the AddGrudge.
<AddGrudge onSubmit={addGrudge} />And then we'll add it to the AddGrudge component.
const handleSubmit = useCallback(
event => {
event.preventDefault();
onSubmit({ person, reason });
setPerson('');
setReason('');
},
[person, reason, onSubmit]
);Let's make an action creator
const forgiveGrudge = useCallback(
id => {
dispatch({
type: FORGIVE_GRUDGE,
payload: {
id
}
});
},
[dispatch]
);We'll also update the reducer here.
if (action.type === FORGIVE_GRUDGE) {
return state.filter(grudge => grudge.id !== action.payload.id);
}We'll thread through forgiveGrudge as onForgive.
<button onClick={() => onForgive(grudge.id)}>Forgive</button>That prop drilling isn't great, but we'll deal with it in a bit.
We need to think about the past, present, and future.
const defaultState = {
past: [],
present: [],
future: []
};We've broken almost everything. So, let's make this a bit better.
const reducer = (state, action) => {
if (action.type === ADD_GRUDGE) {
return {
past: [],
present: [
{
id: uniqueId(),
...action.payload
},
...state.present
],
future: []
};
}
if (action.type === FORGIVE_GRUDGE) {
return {
past: [],
present: state.present.filter(grudge => grudge.id !== action.payload.id),
future: []
};
}
return state;
};past: [state.present, ...state.past]if (action.type === UNDO) {
const [newPresent, ...newPast] = state.past;
return {
past: newPast,
present: newPresent,
future: [state.present, ...state.present]
};
}const undo = useCallback(() => {
dispatch({ type: UNDO });
}, [dispatch]);<button disabled={!state.past.length} onClick={undo}>
Undo
</button>if (action.type === REDO) {
const [newPresent, ...newFuture] = state.future;
return {
past: [state.present, ...state.past],
present: newPresent,
future: newFuture
};
}const useUndoReducer = (reducer, initialState) => {
const undoState = {
past: [],
present: initialState,
future: []
};
const undoReducer = (state, action) => {
const newPresent = reducer(state, action);
if (action.type === UNDO) {
const [newPresent, ...newPast] = state.past;
return {
past: newPast,
present: newPresent,
future: [state.present, ...state.future]
};
}
if (action.type === REDO) {
const [newPresent, ...newFuture] = state.future;
return {
past: [state.present, ...state.past],
present: newPresent,
future: newFuture
};
}
return {
past: [state.present, ...state.past],
present: newPresent,
future: []
};
};
return useReducer(undoReducer, undoState);
};