Create your first React app - Part 4 (Adding and deleting users)

In order to add new users, we'll start by updating the handleAddUser() function in the root component so that it looks like this:

const handleAddUser = (evt) => {
    setSelectedUserId(0);
}

Remember that the handleAddUser() function will be triggered when the button in the root component is clicked. By setting the selectedUserId to 0, it indicates that a new user is being created (0 is not a valid user id, but the data access component will generate a valid id when we insert it into the database - we'll see that soon).

Run the app, and notice that now when you click on the 'Add User' button, the UserForm will be displayed with all of it's text inputs empty. The selectedUserId state in the root component is passed to the userId prop of UserForm component. So when this prop changes, the UserForm will re-render. After it renders, the callback that we passed into useEffect() will NOT invoke getUserById() on the data access component, because the userId prop is 0. This will leave the state variables in UserForm at their default values (which are all initialized to empty strings).

Now, we'll need to update the handleUserSaved() function in the root component. Recall that this function will be triggered in the submit handler of the UserForm (because it gets passed to the UserForm as the onUserSaved prop). The handleUserSaved() function currenlty assumes that we are always updating users, but now we'll add some logic so that it can determine whether to do an update or an insert. Modify the function so that it looks like this:

const handleUserSaved = (updatedUser) => {

    if(updatedUser.id > 0){
        uda.updateUser(updatedUser);
        setUsers(users.map(u => u.id === updatedUser.id ? updatedUser : u))
    }else{
        uda.insertUser(updatedUser);
        setUsers([...users, updatedUser])
    }

    // hide the user form
    setSelectedUserId(-1);

}

If the id of the updatedUser parameter is greater than 0, then we call updateUser() on the data access component, otherwise, we call insertUser().

After using our data access component to make change in the database, we need to update the component's user state, by calling setState(). The parameters that are getting passed into to setUsers() making use of advanced modern JavaScript. You need to have a solid understanding ofthe map() method, and you also need to understand the spread operator. Let's discuss these two lines of code in class!

If you run the app now, you should be able to add new users.

Deleting users

In this step well add a 'Delete' button to the UserForm. But before we do that, we need to declare another prop, which we'll call onUserDeleted. Remember that the 'users' state is declared in the root component. So all changes to users should happen in the root component. So we'll define a function that deletes a user, and pass that into the UserForm as the onUserDelete prop.

Add a new prop to the UserForm() function by updating its first line to look like this:

function UserForm({userId, onUserSaved, onUserDeleted}){

Next, add this function to the root component (put it just befoe the return statement):

const handleUserDeleted = (id) => {
    // delete the user from the database
    uda.deleteUser(id);
    // update the 'users' state
    setUsers(users.filter(user => user.id !== id));
    // hide the user form
    setSelectedUserId(-1);
}

Now we'll pass the handleUserDeleted function to the UserForm as the onUserDeleted prop. Update the JSX returned by the RootCompnent so that it looks like this:

<div>
    <h1>User Manager</h1>
    <button onClick={handleAddUser}>Add User</button>
    <p>Number of users: {users.length}</p>
    <UserList users={users} onUserSelected={handleUserSelected} />
    {selectedUserId > -1 && <UserForm key={selectedUserId} userId={selectedUserId} onUserSaved={handleUserSaved} onUserDeleted={handleUserDeleted} />}
</div>

We've added an 'onUserDeleted' prop to the UserForm element, and set it's value to the handleUserDeleted function (this line of code is getting long and ugly, we can discuss different ways of formatting this code in class).

Now we'll add a 'Delete' button to the UserForm, but we only want to display it when the userId prop is greater than 0. So we'll use 'conditional rendering'. Add this code to the JSX in the UserForm (put it just after the submit button):

{userId > 0 && <input type="button" value="Delete" onClick={() => {onUserDeleted(userId)}} />}

We've added an 'onClick' attribute that triggers an anonymous function. Inside the anonyomous function, we trigger onUserDeleted prop/function. You might be wondering why we need the anonymous function, but this would not work:

onClick={onUserDeleted(userId)}

This would invoke onUserDeleted() when the component renders. We want to invoke it when the button is clicked.

If you run the app now, you should be able to delete users.

And that you've created your first CRUD application in React, I'm assuming that we'll have many things to discuss in our next class meeting!