Skip to content

iamskyy666/rtk-notes-app

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

This is actually a very good small project to understand how RTK Query works in a real React app. Instead of just reading theory, we now have a full flow:

React Components
      ↓
RTK Query Hooks
      ↓
Redux Store Cache
      ↓
API (json-server)

We’ll walk through every file step-by-step and explain the concepts deeply.


1️⃣ Backend Data (json-server)

Our db.json

{
  "notes": [
    {
      "id": "1",
      "title": "1st Note",
      "content": "This is the content of the 1st note"
    },
    {
      "id": "2",
      "title": "2nd Note",
      "content": "This is the content of the 2nd note"
    },
    {
      "id": "3",
      "title": "3rd Note",
      "content": "This is the content of the 3rd note"
    }
  ]
}

This is a fake REST API database.

When we run:

json-server --watch db.json --port 3000

It automatically creates REST endpoints.

Generated endpoints

GET    /notes
GET    /notes/:id
POST   /notes
PATCH  /notes/:id
DELETE /notes/:id

So our React app can interact with it like a real backend.


2️⃣ notesApi.js (The RTK Query API Layer)

This file defines how React talks to the server.

import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

createApi

This is the main function that creates an API service.

It automatically generates:

  • Redux reducers
  • Redux middleware
  • React hooks
  • Caching system

API Configuration

export const notesApi = createApi({
  reducerPath: "notesApi",

reducerPath

This defines where RTK Query state lives in Redux store.

Example store:

store = {
   notesApi: {
      queries: {},
      mutations: {}
   }
}

baseQuery

baseQuery: fetchBaseQuery({ baseUrl: "http://localhost:3000/" })

fetchBaseQuery is a small wrapper around fetch API.

So internally it does something like:

fetch(baseUrl + endpoint)

Example:

baseUrl = http://localhost:3000/
endpoint = notes

Final request:

http://localhost:3000/notes

tagTypes

tagTypes: ["Note"]

Tags are used for automatic cache invalidation.

Think of it like:

Query Cache
    ↓
Tagged with "Note"

When we update/delete/add β†’ RTK Query knows which cached data should be refreshed.


3️⃣ Endpoints

Endpoints define API operations.

Query β†’ GET data
Mutation β†’ change data

4️⃣ getNotes Query

getNotes: builder.query({
  query: () => "notes",
  providesTags: ["Note"],
})

builder.query

Used for fetching data (GET requests).

query

Defines the endpoint path.

GET http://localhost:3000/notes

providesTags

This marks cached data with tag:

"Note"

Meaning:

notes cache β†’ tag "Note"

Later if a mutation invalidates "Note" β†’ query refetches automatically.


5️⃣ addNote Mutation

addNote: builder.mutation({
  query: (newNote) => ({
    url: "notes",
    method: "POST",
    body: newNote,
  }),
  invalidatesTags: ["Note"],
})

Mutation means changing server data.

query function

Returns request configuration.

POST /notes

Body:

{
  title,
  content
}

invalidatesTags

After adding a note:

cache tag "Note" becomes invalid

RTK Query automatically:

refetch getNotes()

So UI updates automatically.


6️⃣ Update Note Mutation

updateName: builder.mutation({
  query: ({ id, ...updatedNote }) => ({
    url: `notes/${id}`,
    method: "PATCH",
    body: updatedNote,
  }),
  invalidatesTags: ["Note"],
})

PATCH request

PATCH /notes/1

Body:

{
 title,
 content
}

This updates the note.

Then:

invalidate "Note" tag
β†’ getNotes refetch

7️⃣ Delete Note Mutation

deleteNote: builder.mutation({
  query: (id) => ({
    url: `notes/${id}`,
    method: "DELETE",
  }),
  invalidatesTags: ["Note"],
})

Request:

DELETE /notes/1

After deletion:

invalidate "Note"
β†’ refetch notes

8️⃣ Auto Generated Hooks

export const {
  useGetNotesQuery,
  useAddNoteMutation,
  useUpdateNameMutation,
  useDeleteNoteMutation,
} = notesApi;

RTK Query automatically creates hooks.

Query hook

useGetNotesQuery()

Mutation hooks

useAddNoteMutation()
useUpdateNameMutation()
useDeleteNoteMutation()

These hooks connect React β†’ Redux β†’ API.


9️⃣ NotesList.jsx

const { data: notes, isLoading } = useGetNotesQuery();

This hook automatically:

1️⃣ sends API request 2️⃣ stores result in Redux cache 3️⃣ re-renders component


Data destructuring

data: notes

Renaming:

data β†’ notes

Loading state

if (isLoading) {
  return <h2>Loading... ⏳</h2>;
}

RTK Query manages loading automatically.


Rendering notes

notes.map((note) => (

Example:

[
 {id:1,title:"1st"},
 {id:2,title:"2nd"}
]

Each note displayed in UI.


Delete note

const [deleteNote] = useDeleteNoteMutation();

Trigger function.

When clicked:

deleteNote(note.id)

Flow:

DELETE request
     ↓
invalidate tag
     ↓
getNotes refetch
     ↓
UI updates

πŸ”Ÿ NoteForm.jsx

Handles creating new notes.


Local state

const [title, setTitle] = useState("");
const [content, setContent] = useState("");

Controlled form inputs.


Mutation hook

const [addNote] = useAddNoteMutation();

Trigger function.


Submit handler

await addNote({ title, content });

Request:

POST /notes

Body:

{
 title,
 content
}

After success:

invalidatesTags
β†’ getNotes refetch

So list updates automatically.


1️⃣1️⃣ EditNote.jsx

Handles updating notes.


Initial state

const [title, setTitle] = useState(note.title);
const [content, setContent] = useState(note.content);

Prefilled form using props.


Mutation hook

Correct version:

const [updateNote, { isLoading, isError, error }] = useUpdateNameMutation();

This returns:

[
 triggerFunction,
 {
   isLoading,
   isError,
   error
 }
]

Submit

await updateNote({
  id: note.id,
  title,
  content
});

Request:

PATCH /notes/1

Disabled UI

disabled={isLoading}

Prevents multiple submissions.


Error UI

{isError && (
  <div>Error: {error.data.message || "Updation-Failed ⚠️"}</div>
)}

Shows mutation errors.


1️⃣2️⃣ App.jsx

This is the root UI.

<NotesList />
<NoteForm />
<EditNote />

Components:

NotesList β†’ show notes
NoteForm β†’ add notes
EditNote β†’ update notes

🧠 The Full Data Flow

When our app loads:

React mounts
     ↓
useGetNotesQuery()
     ↓
RTK Query sends GET /notes
     ↓
Response cached in Redux
     ↓
NotesList renders

When adding note

NoteForm
  ↓
addNote()
  ↓
POST /notes
  ↓
invalidatesTags("Note")
  ↓
getNotes refetch
  ↓
NotesList updates

When deleting

deleteNote(id)
   ↓
DELETE /notes
   ↓
invalidate "Note"
   ↓
getNotes refetch

🧠 Key Concepts This Project Uses

We actually implemented most RTK Query core concepts:

1️⃣ API Slice

createApi()

2️⃣ Queries

builder.query()

Used for fetching.


3️⃣ Mutations

builder.mutation()

Used for modifying data.


4️⃣ Auto Generated Hooks

useQuery
useMutation

5️⃣ Cache Tags

providesTags
invalidatesTags

Automatic cache refresh.


6️⃣ Server State Management

RTK Query manages:

loading
error
data
caching
refetching

Without writing reducers.


About

πŸ“ A mini Note-Taking CRUD app to implement Redux-Toolkit Query. βš›οΈ

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors