React’s useState hook is great for managing simple state. But when multiple state values are related or dependent, useReducer becomes a better choice. In this article, we migrate story-related state from useState to useReducer and explore how to manage multiple transitions declaratively.
Start by defining a reducer outside the component. It receives state and action and returns a new state:
const storiesReducer = (state, action) => {
switch (action.type) {
case 'SET_STORIES':
return action.payload;
case 'REMOVE_STORY':
return state.filter(
(story) => action.payload.objectID !== story.objectID
);
default:
throw new Error();
}
};Replace useState with useReducer in the App component:
const [stories, dispatchStories] = React.useReducer(
storiesReducer,
[]
);Now use dispatchStories to update state instead of setStories.
When data is fetched asynchronously, dispatch an action to set the stories:
getAsyncStories()
.then((result) => {
dispatchStories({
type: 'SET_STORIES',
payload: result.data.stories,
});
});To remove a story, dispatch another action:
const handleRemoveStory = (item) => {
dispatchStories({
type: 'REMOVE_STORY',
payload: item,
});
};Using useReducer in React allows for more structured and scalable state management. By handling multiple transitions like setting and removing stories, we move from imperative logic to declarative control. This approach is especially useful when managing complex or interdependent state in larger applications.