Skip to content
react

How to invalidate query after mutations in React-Query

May 24, 2023Abhishek EH5 Min Read
How to invalidate query after mutations in React-Query

In this article, we will see how to use React-Query to fetch the to do list from API and display it, add a new item to the list, and update the list using mutation and query invalidation.

Building the API

We can use json-server to quickly spin up a server.

Install it globally using the following command:

1npm install -g json-server

Create a json file named db.json in a location of your choice with the following content:

db.json
1{
2 "todos": [
3 {
4 "id": "odnv6g",
5 "title": "Do the dishes"
6 },
7 {
8 "id": "jhomrt",
9 "title": "Publish a blog"
10 }
11 ]
12}

Now start the server using the command:

1json-server --watch db.json --port 8765

The API will spin up at: http://localhost:8765/todos

React app setup

Create a react app using the following command:

1npx create-react-app react-query-invalidate

Now install react-query:

1npm install react-query

In the index.js add react-query provider:

index.js
1import React from "react"
2import ReactDOM from "react-dom/client"
3import "./index.css"
4import App from "./App"
5import { QueryClient, QueryClientProvider } from "react-query"
6const queryClient = new QueryClient()
7
8const root = ReactDOM.createRoot(document.getElementById("root"))
9root.render(
10 <QueryClientProvider client={queryClient}>
11 <App />
12 </QueryClientProvider>
13)

Displaying the todo list

Now let's display the todo list using react-query in App.js.

App.js
1import React from "react"
2import { useQuery } from "react-query"
3
4const App = () => {
5 // Fetch todos
6 const { isLoading, error, data } = useQuery("todos", async () => {
7 const response = await fetch("http://localhost:8765/todos")
8 return response.json()
9 })
10
11 if (isLoading) {
12 return <div>Loading...</div>
13 }
14
15 if (error) {
16 return <div>Error: {error.message}</div>
17 }
18
19 return (
20 <div>
21 <h1>Todo List</h1>
22 <form>
23 <input
24 type="text"
25 name="todoInput"
26 placeholder="Enter a todo"
27 required
28 />
29 <button type="submit">Add Todo</button>
30 </form>
31 <ul>
32 {data.map(todo => (
33 <li key={todo.id}>{todo.title}</li>
34 ))}
35 </ul>
36 </div>
37 )
38}
39
40export default App

In the above code:

  • We are using useQuery hook to fetch the todos from the API.
  • We are displaying a loading message while the data is being fetched.
  • We are displaying an error message if there is an error while fetching the data.
  • We are displaying the todo list if the data is fetched successfully.

Now if you run the app, you will see the following output:

display todo list

Adding a new todo

The next thing to do is to add a new item to the list.

We will write a submit handler for the form:

App.js
1import React from "react"
2import { useMutation, useQuery } from "react-query"
3
4const App = () => {
5 // Fetch todos
6 const { isLoading, error, data } = useQuery("todos", async () => {
7 const response = await fetch("http://localhost:8765/todos")
8 return response.json()
9 })
10
11 // Add todo mutation
12 const addTodoMutation = useMutation(async newTodo => {
13 const response = await fetch("http://localhost:8765/todos", {
14 method: "POST",
15 headers: {
16 "Content-Type": "application/json",
17 },
18 body: JSON.stringify(newTodo),
19 })
20 return response.json()
21 })
22
23 // Handler for submitting the form
24 const handleSubmit = e => {
25 e.preventDefault()
26 const { value } = e.target.todoInput
27 const newTodo = {
28 id: Math.random().toString(36).substring(7), // Generate a random alphanumeric id
29 title: value,
30 }
31 addTodoMutation.mutate(newTodo)
32 e.target.reset()
33 }
34
35 if (isLoading) {
36 return <div>Loading...</div>
37 }
38
39 if (error) {
40 return <div>Error: {error.message}</div>
41 }
42
43 return (
44 <div>
45 <h1>Todo List</h1>
46 <form onSubmit={handleSubmit}>
47 <input
48 type="text"
49 name="todoInput"
50 placeholder="Enter a todo"
51 required
52 />
53 <button type="submit">Add Todo</button>
54 </form>
55 <ul>
56 {data.map(todo => (
57 <li key={todo.id}>{todo.title}</li>
58 ))}
59 </ul>
60 </div>
61 )
62}
63
64export default App

In the above code:

  • addTodoMutation function is used to make API call and add a new todo item to the list.
  • In handleSubmit function, we are generating a random id for the new todo item and calling the addTodoMutation function.

Now if we run the application and add a new item to the list, it won't be reflected in the UI.

However, it will be saved in db.json file.

db.json
1{
2 "todos": [
3 {
4 "id": "odnv6g",
5 "title": "Do the dishes"
6 },
7 {
8 "id": "jhomrt",
9 "title": "Publish a blog"
10 },
11 {
12 "id": "7v72h",
13 "title": "Mow the lawn"
14 }
15 ]
16}

Updating the list using query invalidation

The final step is to update the UI when a new item is added.

We can use the onSuccess callback of useMutation hook to invalidate the query.

App.js
1import React from "react"
2import { useQuery, useMutation, useQueryClient } from "react-query"
3
4const App = () => {
5 const queryClient = useQueryClient()
6
7 // Fetch todos
8 const { isLoading, error, data } = useQuery("todos", async () => {
9 const response = await fetch("http://localhost:8765/todos")
10 return response.json()
11 })
12
13 // Add todo mutation
14 const addTodoMutation = useMutation(
15 async newTodo => {
16 const response = await fetch("http://localhost:8765/todos", {
17 method: "POST",
18 headers: {
19 "Content-Type": "application/json",
20 },
21 body: JSON.stringify(newTodo),
22 })
23 return response.json()
24 },
25 {
26 onSuccess: () => {
27 queryClient.invalidateQueries("todos")
28 },
29 }
30 )
31
32 // Handler for submitting the form
33 const handleSubmit = e => {
34 e.preventDefault()
35 const { value } = e.target.todoInput
36 const newTodo = {
37 id: Math.random().toString(36).substring(7), // Generate a random alphanumeric id
38 title: value,
39 }
40 addTodoMutation.mutate(newTodo)
41 e.target.reset()
42 }
43
44 if (isLoading) {
45 return <div>Loading...</div>
46 }
47
48 if (error) {
49 return <div>Error: {error.message}</div>
50 }
51
52 return (
53 <div>
54 <h1>Todo List</h1>
55 <form onSubmit={handleSubmit}>
56 <input
57 type="text"
58 name="todoInput"
59 placeholder="Enter a todo"
60 required
61 />
62 <button type="submit">Add Todo</button>
63 </form>
64 <ul>
65 {data.map(todo => (
66 <li key={todo.id}>{todo.title}</li>
67 ))}
68 </ul>
69 </div>
70 )
71}
72
73export default App

Now if you add a new item to the list, it will be reflected in the UI. You can check the network tab to see both the create and fetch API calls.

create and fetch api calls

Source code

You can download the complete code from here.

Do follow me on twitter where I post developer insights more often!

© 2024 CodingDeft.Com