If simply reading this tutorial isn't enough for you, you can go here and write code alongside it.
Now, we are faced with a new challenge.
We need to implement the feature of editing existing users. Of course, we could use the AddUserForm
component, but let's not complicate things and create a separate form for editing, which we will show under a certain condition.
First, let's start by adding a new field to our table, which will contain an edit button.
components/users-table.js
import {
...other imports
Button
} from "@mui/material";
export const UsersTable = () => {
const { users, createEditUserTemplate } = useUsersContext();
const handleEditUserAction = (user) => {
createEditUserTemplate(user);
}
const renderUsers = (users) => {
return users.map(user => {
return (
<TableRow key={user.id}>
<TableCell>{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<Button
variant="contained"
color="info"
onClick={() => handleEditUserAction(user)}>
Edit
</Button>
</div>
</TableCell>
</TableRow>
);
})
}
return (
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell>Email</TableCell>
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{renderUsers(users)}
</TableBody>
</Table>
</TableContainer>
);
}
Here, we have added a new Actions
column to our user table. Now, each user has an Edit
button, which, when clicked, passes the user's data to the editing template.
Let's now implement this in the UsersProvider
.
components/users-provider.js
...code
export const UsersProvider = ({ data, children }) => {
...code
const [editUserTemplate, setEditUserTemplate] = useState(null);
...code
const createEditUserTemplate = (user) => {
setEditUserTemplate(user);
}
const editUser = (userToEdit) => {
setUsers(users => {
return users.map(user => {
if (user.id === userToEdit.id) {
user.name = userToEdit.name;
user.email = userToEdit.email;
}
return user;
});
});
setEditUserTemplate(null);
}
return (
<UsersContext.Provider value={{
users,
addUser,
editUser,
editUserTemplate,
createEditUserTemplate
}}>
{children}
</UsersContext.Provider>
);
}
Here, we define two new functions:
-
editUser
: finds the user that needs to be updated and returns a new list -
createEditUserTemplate
: simply adds data to the editing template that we are currently using further.
Now, let's implement form for user editing.
components/edit-user-form.js
import { useEffect } from "react";
import { useInput } from "../hooks/use-input";
import { useUsersContext } from "./users-provider";
import { Button, TextField } from "@mui/material";
export const EditUserForm = () => {
const [nameProps, nameActions] = useInput('');
const [emailProps, emailActions] = useInput('');
const { editUser, editUserTemplate } = useUsersContext();
useEffect(() => {
const { name, email, company } = editUserTemplate;
nameActions.update(name);
emailActions.update(email);
}, [editUserTemplate]);
const onSubmit = (e) => {
e.preventDefault();
editUser({
id: editUserTemplate.id,
name: nameProps.value,
email: emailProps.value,
})
}
return (
<form onSubmit={onSubmit} style={{ margin: '24px 0' }}>
<TextField
{...nameProps}
fullWidth
type="text"
label="Name"
variant="outlined"
/>
<br />
<br />
<TextField
{...emailProps}
fullWidth
type="email"
label="Email"
variant="outlined"
/>
<div style={{ textAlign: 'right', marginTop: 12 }}>
<Button
variant="contained"
color="primary"
type="submit">
Save
</Button>
</div>
</form>
);
}
This form has almost the same functionality as the AddUserForm
, except that we update the input fields' state based on the data taken from the editUserTemplate
.
The final touch is to tell the Users
component which form we currently want to show the user.
components/users.js
import { Container } from '@mui/material';
import { UsersTable } from './users-table';
import { AddUserForm } from './add-user-form';
import { EditUserForm } from './edit-user-form';
import { useUsersContext } from './users-provider';
export const Users = () => {
const { editUserTemplate } = useUsersContext();
return (
<Container
maxWidth="md"
sx={{ margin: '20px auto' }}>
{editUserTemplate ? <EditUserForm /> : <AddUserForm />}
<UsersTable />
</Container>
);
}
Finally, we are on the home stretch. We've become a bit familiar with how to build a user-friendly layout using Material UI
. We've learned how to effectively fetch data from an API using React Query
, and we've also set up the overall architecture for our application, small as it may be.
Now it's time to implement user deletion to wrap things up.
First off all, let's add a Delete
button for each row in UsersTable
.
components/users-table.js
...imports
export const UsersTable = () => {
const { users, createEditUserTemplate, deleteUser } = useUsersContext();
...code
const handleDeleteUserAction = (user) => {
deleteUser(user);
}
const renderUsers = (users) => {
return users.map(user => {
return (
<TableRow key={user.id}>
<TableCell>{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<Button
variant="contained"
color="info"
onClick={() => handleEditUserAction(user)}>
Edit
</Button>
<Button
variant="contained"
color="error"
onClick={() => handleDeleteUserAction(user)}>
Delete
</Button>
</div>
</TableCell>
</TableRow>
);
})
}
...code
}
The logic is very similar to what we did earlier. We just take data about user and execute function we received from useUsersContext
.
components/users-provider.js
...code
export const UsersProvider = ({ data, children }) => {
...code
const deleteUser = (userToDelete) => {
setUsers(users => {
return users.filter(user => user.id !== userToDelete.id)
});
setEditUserTemplate(null);
}
return (
<UsersContext.Provider value={{
users,
addUser,
editUser,
editUserTemplate,
createEditUserTemplate,
deleteUser
}}>
{children}
</UsersContext.Provider>
);
}
export const useUsersContext = () => useContext(UsersContext);
That's it! Our application is working as intended, I hope you enjoyed it.
Top comments (0)