React 18 Post Data with Redux Toolkit RTK Query Tutorial

Last Updated on by in React JS

In this comprehensive tutorial, we will learn how to post data using REST API in React application with the help of RTK Query and Redux api slice.

We will teach you how to get the data from the server using the Redux query endpoint. Not only but also, we will learn how to refresh the data when the new data is added to the current data through mutation and auto fetch techniques.

RTK Query is a powerful data fetching and caching tool. It is very useful for loading and caching data in an application, and it offers sturdy ways to deal with the asynchronous tasks.

In order to create new data in React Redux, we need to follow some advanced patterns, properties and methods. We will use builder mutation, It is a function that executes an asynchronous task and returns a promise.

Let us checkout the following steps.

How to Post Data using RTK Query Mutation Endpoint in React Redux

  • Step 1: Install React App
  • Step 2: Install Required Libraries
  • Step 3: Build JSON Server
  • Step 4: Create Endpoints with RTK Query
  • Step 5: Register ApiSlice to Redux Store
  • Step 6: Wrap App using ApiProvider
  • Step 7: Add New Post with Mutation Endpoint
  • Step 8: Update App.js File
  • Step 9: Start Development Server

Install React App

Below is the best way to install a new react app, just run the following command.

npx create-react-app react-rtk-post-endpoint-example

Type and Run the command to go inside the app folder.

cd react-rtk-post-endpoint-example

Install Required Libraries

You have to run the suggested command for installing react redux, redux toolkit and bootstrap CSS library.

npm install react-redux @reduxjs/toolkit bootstrap --legacy-peer-deps

Build JSON Server

We will create RTK Query endpoints to fetch and post data using a RESTful API server.

To make the server, we will use the JSON server module, which allows us to create a fake server that mocks a real server.

npm install -g json-server --legacy-peer-deps

Now, we have to make a new file at the react project root, name it db.json and then put the given data into the file.

{
  "posts": [
    {
      "id": 1,
      "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
      "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
    }
  ]
}

We can now run the backend server using the suggested command.

json-server --watch db.json

Create Endpoints with RTK Query

Head over to src/ folder, make the ‘features’ folder, inside this folder create ‘api’ directory, then make features/api/apiSlice.js file and put the give code into the file.

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

export const apiSlice = createApi({
  reducerPath: 'apiSlice',
  baseQuery: fetchBaseQuery({
    baseUrl: 'http://localhost:3000',
  }),
  tagTypes: ['Post'],
  endpoints: (builder) => ({
    getPosts: builder.query({
      query: () => '/posts',
      providesTags: ['Post'],
    }),
    addNewPost: builder.mutation({
      query: (payload) => ({
        url: '/posts',
        method: 'POST',
        body: payload,
        headers: {
          'Content-type': 'application/json; charset=UTF-8',
        },
      }),
      invalidatesTags: ['Post'],
    }),
  }),
})

export const { useGetPostsQuery, useAddNewPostMutation } = apiSlice

We defined the reducerPath ​ with apiSlice; it’s a unique key used when your service is mounted in your store. Set the base query using the base URL.

RTK Query allows us to define consensus between queries and mutations that enable automatic data refetching, using “tags”.

A “tag” is a string that allows you to name specific types of data and invalidate portions of the cache. When you define invalidated, RTK Query will automatically refetch the endpoints that were marked with that tag.

Basic tag usage requires adding three pieces of information to our API slice:

  • A root tagTypes field in the API slice object, declaring an array of string tag names for data types such as ‘Post.’
  • A providesTags array in query endpoints, listing a set of tags describing the data in that query.
  • An invalidatesTags array in mutation endpoints, listing a set of tags that are invalidated every time that mutation runs

In the above code example, we defined the code with which you can add a new post to the database through a POST request, retrieve all the posts, and refresh all the data in React component when adding new data to the database.

Register ApiSlice to Redux Store

In this section, you need to make the ‘src/app’ folder with ‘store.js’ file. To connect api slice to the store add the given code into the src/app/store.js file.

import { configureStore } from '@reduxjs/toolkit'
import { setupListeners } from '@reduxjs/toolkit/query'
import { apiSlice } from '../features/api/apiSlice'

export const store = configureStore({
  reducer: {
    [apiSlice.reducerPath]: apiSlice.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(apiSlice.middleware),
})

setupListeners(store.dispatch)

Wrap App using ApiProvider

Open the index.js file that you can find inside your react app, wrap the App property with ApiProvider and pass apiSlice to api object.

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'

import { ApiProvider } from '@reduxjs/toolkit/dist/query/react'
import { apiSlice } from './features/api/apiSlice'

const root = ReactDOM.createRoot(document.getElementById('root'))

root.render(
  <React.StrictMode>
    <ApiProvider api={apiSlice}>
      <App />
    </ApiProvider>
  </React.StrictMode>,
)

Add New Post with Mutation Endpoint

We have created the RTK Query POST endpoint and RTK Query GET endpoint. In this step, we will look at how to consume the RTK Query endpoints in features/posts/PostsList.js file.

We will create a small form using Bootstrap, import the api slice hooks for fetching and posting data.

import React from 'react'
import { useGetPostsQuery, useAddNewPostMutation } from '../api/apiSlice'

const PostCard = ({ content }) => {
  return (
    <div className="col-lg-12 mb-3" key={content.id}>
      <div className="card alert alert-success">
        <div className="card-body">
          <h5 className="card-title">{content.title}</h5>
          <p className="card-text">{content.body}</p>
        </div>
      </div>
    </div>
  )
}

function PostsList() {
  let formSubmitError
  const [addNewPost, response] = useAddNewPostMutation()

  const [postForm, setPostForm] = React.useState('Submit')

  const onSubmit = (e) => {
    e.preventDefault()
    const { title, body } = e.target.elements
    let formData = {
      title: title.value,
      body: body.value,
    }

    addNewPost(formData)
      .unwrap()
      .then(() => {})
      .then((error) => {
        console.log(error)
      })
  }

  const {
    data: posts,
    isLoading: isGetLoading,
    isSuccess: isGetSuccess,
    isError: isGetError,
    error: getError,
  } = useGetPostsQuery({ refetchOnMountOrArgChange: true })

  let postContent

  if (isGetLoading) {
    postContent = (
      <div className="d-flex justify-content-center">
        <div className="spinner-border" role="status">
          <span className="visually-hidden">Loading...</span>
        </div>
      </div>
    )
  } else if (isGetSuccess) {
    postContent = posts.map((item) => {
      return <PostCard content={item} key={item.id} />
    })
  } else if (isGetError) {
    postContent = (
      <div className="alert alert-danger" role="alert">
        {getError}
      </div>
    )
  }

  return (
    <div>
      {formSubmitError}
      <div className="d-flex justify-content-center mb-4">
        <div className="col-md-4 offset-md-*">
          <form onSubmit={onSubmit}>
            <div className="mb-3">
              <label className="form-label">
                <strong>Enter Title</strong>
              </label>
              <input type="text" className="form-control" id="title" />
            </div>
            <div className="mb-3">
              <label className="form-label">
                <strong>Enter content</strong>
              </label>
              <textarea className="form-control" id="body" rows="3"></textarea>
            </div>
            <div className="d-grid">
              <button className="btn btn-danger" type="submit">
                {postForm}
              </button>
            </div>
          </form>
        </div>
      </div>

      {postContent}
    </div>
  )
}

export default PostsList

Update App.js File

Go to src/App.js file and add the PostList component in order to show and test the app.

import '../node_modules/bootstrap/dist/css/bootstrap.min.css'
import './App.css'
import PostsList from './features/posts/PostsList'

function App() {
  return (
    <div className="container">
      <div className="d-flex border-bottom pt-2 pb-2 mb-5">
        <div className="p-2 flex-grow-1 text-center">
          <h2>React RTK Query Post Data Example</h2>
        </div>
      </div>

      <div className="row">
        <PostsList />
      </div>
    </div>
  )
}

export default App

Start Development Server

We are going to run the react application, make sure to start the development server by running the given command.

npm start
http://localhost:3000

React Post Data with Redux Toolkit RTK Query Tutorial

Conclusion

In this guide, we understood how to use the RTK query best practices for adding and getting data in React redux store in response to api slice.

This guide is undoubtedly going to help us understand how to automate re-fetching data using RTK query. We took the help of refetchOnMountOrArgChange property and providesTags.

Seldom, we meet with the situation where we could not deal with multiple Query hooks in same component (isLoading, isSuccess, isError). Consequently, we answered how to use multiple Query hooks from redux-toolkit in the same component?

Download the full code of this tutorial from GitRepo.