CRUD Operations
Adding new tasks
Now that we can see the list of tasks, it's time to add a few more.
Add the highlighted newTaskTitle
state and addTask
function the App Component
// src/App.tsx
export default function App() {
const [tasks, setTasks] = useState<Task[]>([])
const [newTaskTitle, setNewTaskTitle] = useState("")
const addTask = async (e: FormEvent) => {
e.preventDefault()
try {
const newTask = await taskRepo.insert({ title: newTaskTitle })
setTasks([...tasks, newTask])
setNewTaskTitle("")
} catch (error: unknown) {
alert((error as { message: string }).message)
}
}
//...
- the call to
taskRepo.insert
will make a post request to the server, insert the new task to thedb
, and return the newTask
object with all it's info (including the id generated by the database)
Import FormEvent
This code requires adding an import of FormEvent
from react
.
Next let's adjust the tsx
to display a form to add new tasks
// src/App.tsx
return (
<div>
<h1>Todos</h1>
<main>
<form onSubmit={addTask}>
<input
value={newTaskTitle}
placeholder="What needs to be done?"
onChange={e => setNewTaskTitle(e.target.value)}
/>
<button>Add</button>
</form>
{tasks.map(task => {
return (
<div key={task.id}>
<input type="checkbox" checked={task.completed} />
{task.title}
</div>
)
})}
</main>
</div>
)
Try adding a few tasks to see how it works
Mark Tasks as completed
Modify the contents of the tasks.map
iteration within the App
component to include the following setCompleted
function and call it in the input's onChange
event.
// src/App.tsx
{
tasks.map(task => {
const setTask = (value: Task) =>
setTasks(tasks => tasks.map(t => (t === task ? value : t)))
const setCompleted = async (completed: boolean) =>
setTask(await taskRepo.save({ ...task, completed }))
return (
<div key={task.id}>
<input
type="checkbox"
checked={task.completed}
onChange={e => setCompleted(e.target.checked)}
/>
{task.title}
</div>
)
})
}
- The
setTask
function is used to replace the state of the changed task in thetasks
array - The
taskRepo.save
method update thetask
to the server and returns the updated value
Rename Tasks and Save them
To make the tasks in the list updatable, we'll bind the tasks
React state to input
elements and add a Save button to save the changes to the backend database.
Modify the contents of the tasks.map
iteration within the App
component to include the following setTitle
and saveTask
functions and add an input
and a save button
.
// src/App.tsx
{
tasks.map(task => {
const setTask = (value: Task) =>
setTasks(tasks => tasks.map(t => (t === task ? value : t)))
const setCompleted = async (completed: boolean) =>
setTask(await taskRepo.save({ ...task, completed }))
const setTitle = (title: string) => setTask({ ...task, title })
const saveTask = async () => {
try {
setTask(await taskRepo.save(task))
} catch (error: unknown) {
alert((error as { message: string }).message)
}
}
return (
<div key={task.id}>
<input
type="checkbox"
checked={task.completed}
onChange={e => setCompleted(e.target.checked)}
/>
<input value={task.title} onChange={e => setTitle(e.target.value)} />
<button onClick={saveTask}>Save</button>
</div>
)
})
}
- The
setTitle
function, called from theinput
'sonChange
event, saves the value from theinput
to thetasks
state. - The
saveTask
function, called from thebutton
's'onClick
event, saves thetask
object to the backend.
Make some changes and refresh the browser to verify the backend database is updated.
Browser's Network tab
As you play with these CRUD
capabilities, monitor the network tab and see that they are all translated to rest
api calls.
Delete Tasks
Let's add a Delete button next to the Save button of each task in the list.
Add the highlighted deleteTask
function and Delete button
Within the tasks.map
iteration in the return
section of the App
component.
// src/App.tsx
{
tasks.map(task => {
const setTask = (value: Task) =>
setTasks(tasks => tasks.map(t => (t === task ? value : t)))
const setCompleted = async (completed: boolean) =>
setTask(await taskRepo.save({ ...task, completed }))
const setTitle = (title: string) => setTask({ ...task, title })
const saveTask = async () => {
try {
setTask(await taskRepo.save(task))
} catch (error: unknown) {
alert((error as { message: string }).message)
}
}
const deleteTask = async () => {
try {
await taskRepo.delete(task)
setTasks(tasks.filter(t => t !== task))
} catch (error: unknown) {
alert((error as { message: string }).message)
}
}
return (
<div key={task.id}>
<input
type="checkbox"
checked={task.completed}
onChange={e => setCompleted(e.target.checked)}
/>
<input value={task.title} onChange={e => setTitle(e.target.value)} />
<button onClick={saveTask}>Save</button>
<button onClick={deleteTask}>Delete</button>
</div>
)
})
}