The previous badge state.
The lifecycle event to apply.
The next state and the badge output to render.
reduceBadge({ isSearching: false, count: 0 }, { type: SearchEvent.STARTED });
// => { state: { isSearching: true, count: 0 }, output: { kind: "animate" } }
reduceBadge({ isSearching: true, count: 0 }, { type: SearchEvent.COMPLETED, count: 0 });
// => { state: { isSearching: false, count: 0 }, output: { kind: "clear" } }
export function reduceBadge(
state: BadgeState,
event: BadgeEvent,
): { state: BadgeState; output: BadgeOutput } {
switch (event.type) {
case SearchEvent.STARTED: {
const next = { isSearching: true, count: 0 };
return { state: next, output: { kind: 'animate' } };
}
case SearchEvent.RESULTS_COUNT: {
const next = { ...state, count: event.count };
return { state: next, output: render(next) };
}
case SearchEvent.COMPLETED: {
const next = { isSearching: false, count: event.count };
return { state: next, output: render(next) };
}
case SearchEvent.ABORTED:
case SearchEvent.FAILED:
case IDB_SEARCH_RESULTS_CLEARED: {
const next = { isSearching: false, count: 0 };
return { state: next, output: { kind: 'clear' } };
}
default:
return { state, output: render(state) };
}
}
Pure reducer: given the previous BadgeState and an event, returns the next state and the resulting BadgeOutput. No side effects, so it can be unit-tested without a DOM or chrome.action mock.
The single rule the rest of the app used to get wrong: a count of 0 keeps the ellipsis while a search is in flight, but clears the badge once the search has ended (or when no search is running at all, e.g. popup open).