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);
};