In this task, we will update the UI to facilitate CRUD operations for the posts app. We will use the API calls we created in the previous task to fetch, create, update, and delete posts.
Step 1: Read all posts
Let’s start by updating the Posts component to fetch all posts from the API and display them in the UI. Currently, we are using a static list of posts from the data/db.json file. We will replace this with an API call to fetch the posts.
When the component mounts, we fetch the posts from the API and update the state with the fetched data.
Step 2: Delete a post
When you click on the trash icon, we want to trigger the deletion of the corresponding post. To achieve this, we’ll call the deletePost function, which makes an API request to remove the post from the list.
To execute the deletePost function, we need to pass the unique identifier (post.id) as an argument. To do this, we can pass the entire post object as a prop to the PostActions component, allowing us to access and use the id property when needed.
First, update the Post component to pass the post object to the PostActions component.
Next, update the PostActions component to accept the post object as a prop and call the deletePost function when the trash icon is clicked.
Run the program and click on the trash icon next to one of the posts. You’ll notice that it doesn’t disappear from the list immediately. To verify, check the api/db.json file to see that the post has indeed been deleted from the “database.” However, the UI still displays the old list of posts because we haven’t updated the state to reflect the changes. You can refresh the page to see the updated list of posts.
To make this behavior more efficient, we don’t want to require a page refresh to see the changes. Instead, we can update the state of our posts directly. To achieve this, let’s pass the setPosts function from the Posts component down to the Post component, and then further down to the PostActions component. This will enable us to update the state directly when a post is deleted, without having to re-fetch the entire list of posts.
First, update the Posts component to pass the setPosts function to the Post component.
Next, update the Post component to pass the setPosts function to the PostActions component.
Finally, update the PostActions component to call the setPosts function after deleting a post.
Now if you delete a post by clicking on the trash icon, it will be removed from the list without having to refresh the page.
Notice how we use the setPosts function to update the state of the posts. Instead of directly passing the new list of posts, we pass a function that receives the previous state and returns the new state. This enables us to filter out the post that was deleted from the list of posts.
Moreover, notice the type of the setPosts function. It is React.Dispatch<React.SetStateAction<PostType[]>>. We are using the type React.Dispatch to define the type of the function that can be used to update the state. The React.SetStateAction type is a utility type provided by React that represents the type of the state setter function. In this case, it is a function that takes the previous state as an argument and returns the new state.
Step 3: Add confirmation dialog
It is possible that a user might accidentally click on the trash icon and delete a post. To prevent this, we can add a confirmation dialog before deleting a post.
To achieve this, we will use an alert dialog component that will display a confirmation message before deleting the post. The shadcn UI library provides an AlertDialog component that we can use for this purpose.
Create a new component delete-post-dialog.tsx in the components folder.
Notice the AlertDialog component from the shadcn UI library. It provides a way to display an alert dialog with a title, description, and action buttons. The AlertDialogTrigger component is used to trigger the alert dialog. In this case, we are using a button with the trash icon as the trigger. The AlertDialogContent component contains the content of the alert dialog, including the title, description, and action buttons. The AlertDialogAction component is used to define the action button that will be triggered when clicked. In this case, we are calling the handleDelete function to delete the post. The AlertDialogCancel component is used to define the cancel button that will close the alert dialog.
The asChild prop on the AlertDialogTrigger component is a pattern used in Radix UI which Shadcn UI is built on. This prop delegates the rendering of a component to its single child element. In the case of the AlertDialogTrigger component, this allows us to use any element as the trigger for the alert dialog. For a deeper understanding of how asChild works, I recommend reading this article: asChild in React, Svelte, Vue, and Solid for render delegation.
Update the PostActions component to use the DeletePostDialog component.
Now if you click on the trash icon, an alert dialog will appear asking you to confirm the deletion of the post. If you click on “Continue,” the post will be deleted. If you click on “Cancel,” the post will not be deleted.
Step 4: Create a new post
Let’s add a form to create a new post. We will create a new component add-post.tsx in the components folder. We will use the Textarea and Button components from the shadcn UI library.
Add the following code to the add-post.tsx file.
We can add the AddPost component to the Feed component, before displaying the list of posts.
Now if you run the program, you will see the form to create a new post. However, if you add a new post, it will not be displayed in the list of posts. This is because we are not updating the state of the posts to reflect the changes.
However, if you refresh the page, you will see the new post. In the next step, we will update the state of the posts to reflect the changes when a new post is added.
Step 5: Lift up the state
To update the state of the posts when a new post is added, we need to lift up the state from the Posts component to the Feed component. We will pass the setPosts function to the AddPost component and update the state when a new post is created.
Update the Posts component to accept the posts data and setPosts function as props.
Notice I added the line // eslint-disable-next-line react-hooks/exhaustive-deps to disable the exhaustive-deps rule for this effect. This is because the setPosts function is not a dependency of the effect, and we don’t want to trigger the effect every time setPosts changes. However, ESLint will warn you about this. You can disable the warning for this line by adding the comment. Be cautious when disabling this rule, as it can lead to bugs if not handled properly.
Next, update the AddPost component to accept the setPosts function as a prop and update the state when a new post is created.
Notice in handleSave, we are calling the createPost function to create a new post. Once the post is created successfully, we update the state of the posts by adding the new post to the list.
Now if you add a new post, it will be added to the list without having to refresh the page.
Step 6: Hide the add post form
It would make sense to hide the add post form when not in use. Let’s add a state to show or hide the add post form. Do this in the App component because we need to share this state with the Sidebar and Feed components. Make sure to pass the showAddPost and setShowAddPost function to the Sidebar and Feed components.
Update the Sidebar component to show the add post button when the showAddPost state is false (i.e., the add post form is not visible). Moreover, when this button is clicked, it should call the setShowAddPost function to show the add post form.
Now, update the Feed component to show or hide the add post form based on the showAddPost state. Moreover, pass the setShowAddPost function to the AddPost component.
Finally, update the AddPost component to hide the form (change the showAddPost state) when the post or cancel buttons are clicked.
Now, the add post form will be hidden when not in use and will be displayed when the “Make a Post” button is clicked. Moreover, the form will be hidden when a new post is created or when the cancel button is clicked.
Step 7: Update a post
We will use a form similar to the add post form to update a post. Let’s create a new component edit-post.tsx in the components folder to edit a post.
Next, update the Post component to define the isEditing state and show the EditPost component when the isEditing state is true. Pass the setIsEditing function to the EditPost component and the PostActions component.
Next, update the PostActions component to set the isEditing state to true when the pencil icon is clicked.
Now, if you click on the pencil icon next to a post, the post content will be replaced with a form to edit the post. You can make changes to the post content and click on the “Post” button to save the changes. The post content will be updated without having to refresh the page.
Conclusion
In this task, we implemented CRUD operations for the posts app. We updated the UI to fetch, create, update, and delete posts using API calls. We also added a confirmation dialog before deleting a post and a form to create and edit posts. By lifting up the state and sharing it between components, we were able to update the UI efficiently without requiring a page refresh.