Build a comment system with only 1 React hook.
Lately, I have been discovering the amazing possibilities of the wide variety of hooks in React. That’s why every week I will be posting about a different hook and an example of how to use it. Today we’ll check out the useReducer hook. If you’re knowledgeable with Redux you’ll certainly recognize the same patterns being applied here. So without further ado, let’s get started.
A basic comment system typically has the following functionality (although it can be much more):
1. Fetching the comments for the specific post
2. Being able to add new comments
3. Being able to delete comments
Start by creating a new React app:
In the source folder create a new file called Comments.js. For now, we’ll just make this an empty functional component. Clean up your App component and add the Comments component as the only child of the App.
How does this useReducer hook work? Calling the useReducer hook requires 2 things: A reducer (this is a function that will handle calls to the hook) and an initial state. A reducer is a function that will take some input and return a new state. Let’s start by defining the reducer as a separate function in the Comments.js component:
As you can see it’s quite a big block of code. Let’s break it down so you understand what’s happening. The commentReducer takes 2 inputs on line 1: 1. the state (compare this to a previous state object when trying to update information in a class component). 2. The action. This is an object that contains a type and a payload. The reducer should return a new state object that can be used through your application.
In the reducer function, a switch statement is used with the action type as input. This is because we want to differentiate between the different types of actions and do different things with our payload. e.g. the ‘COMMENTS_LOADED’ type will update the comments with the action’s payload, the ‘ADD_COMMENT’ action will add a new comment to our list of comments. The ‘REMOVE_COMMENT’ will remove the comment with the id that is in our payload. Finally, if the action type is not supported we throw an exception.
Now that we have the reducer down, let’s hook it up (pun not intended).
You can see that we do multiple things in our Comment.js, first, on line 7, we get the comments we passed as a prop from within App.js. This prop contains 2 comments with both an id and some text. This prop then gets used in the useReducer function where we pass our reducer and our initial state with comments equal to the comments we get from the props. I realize I could have mimicked a API-fetch here but decided not to because I’d have to rename my post to “Create a comment system using 2 reacthooks” as I would need the useEffect hook to get my point across. That’s why we’re fetching the posts with a button click
The useReducer function returns a state and a dispatch method which will allow us to update the state. The state will be equal to the initial state we’ve passed to the useReducer hook until we call the dispatch function.
On the rendering side, from line 11, we check if the showComments flag on the state is set and return the comments with some formatting if that’s the case. Note that for clarity sake I used an if-else statement. In real applications you would probably use a conditional operator.
If the showComments flag is not set, we render a button with an onClick method that will dispatch an event to our reducer of type “COMMENTS_LOADED”. This event is simply an object with a field called type, which we pass to the dispatch function. If you scroll back to our reducer you’ll see that the reducer in the switch statement will return an object with the showComments flag set to true. This will trigger a re-render of the component and now, the comments will be visible. You can see this functionality below:
Now let’s finish up our component by adding a input field and a button as way of inserting comments, and add a simple button per comment to delete the comment.
In the script above 2 new things were added. 1 on line 50, a new delete button was added that has an onClick method that will dispatch an object with a type-field “REMOVE_COMMENT” and a payload that contains the id. In the reducer, this id will be used to filter the state comments and throw out the comment with the id passed to the dispatch function.
The next addition, an input on line 55, will call a method called “handleInputKeyUp” on line 29 on every keyUp event. This function will check whether the last pressed key was enter and if so, dispatch an object with a type-field called “ADD_COMMENT” and the text as a payload. The reducer will then create a new comment with a new id which will allow us to remove it.
The final result:
I hope you enjoyed this article and learned a little bit about the useReducer hook and how, together with react State, can offer great possibilities to update multiple complex parts of your react application.