How To Post And Fetch Data Using React-query

·

10 min read

How To Post And Fetch Data Using React-query

Introduction

In this article, you will learn how to setup react-query to fetch and post data in your applications written in typescript/javascript and react functional components. Furthermore, you will learn what react-query is and its advantages.

What is react-query?

React-query is a great library that solves the problem of managing server state and caching in applications, according to the official documentation "React Query is often described as the missing data-fetching library for React, but in more technical terms, it makes fetching, caching, synchronizing, and updating server state in your React applications a breeze." For more information, see react-query.

Advantages of using react-query

Improves application performance with data caching. Furthermore, allows for data to be fetched in the background whenever there is an update(background updating),refetches data when a window is refocused, and also provides defaults that retry a failed query request three(3) times in cases where there is an error.

Firstly, let's install react-query version 3.5.12 with npm or yarn

npm i react-query
or
yarn add react-query

Install material ui, mui-datatables, and react-hook-form which will be used for styling, listing, and form validation, for more information please visit the respective docs.

npm install @material-ui/core
npm install mui-datatables --save
npm install react-hook-form

Install React Query Devtools with the snippet below, this is optional.

yarn add react-query-devtools

Configure client provider

Now let's configure the app.tsx component by wrapping QueryClientProvider because it needs to be on top of components that are responsible for fetching data. QueryClientProvider should take an instance of QueryClient, QueryClient can be used to configure and interact with QueryCache and MutationCache instances.

import React from 'react'
import  queryClient from './clientProvider/clientProvider';
import { QueryClientProvider } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools';
import UserList from './components/userList';
import ResponsiveDrawer from './components/drawer';
const App=()=> {
  return (
    <QueryClientProvider client={queryClient}>
      <div className="App">
        <ResponsiveDrawer/>
        <UserList/>
      </div>
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}

export default App;

Posting data with axios

Let's jump in and create the component called employeeForm.tsx responsible for posting data, We are going to use a dummy rest api called beeceptor for the post request with axios. Learn about beeceptor. Create a function that contains the post request like in the code snippet below.

const createEmployee = async (data: Employee) => {
const { data: response } = await axios.post('https://employee.free.beeceptor.com/create', data);
return response.data;
};

Now create an employee interface of any number of fields.

interface Employee{
    name:string;
    job:string;
    id:string;
}

In our functional component, we are going to import a hook from react-query called useQueryClient which returns a QueryClient instance. useQueryClient hook will be used to invalidate queries, after this happens a query is considered stale and can also be refetched in the background if it is currently being rendered through related hooks.

import {useQueryClient} from 'react-query'
const queryClient = useQueryClient()

Next, we import useMutation hook which is used for any CRUD operations in your application. We will destructure mutate and isLoading from the useMutation hook, the hook will take a parameter which is a function responsible for posting, and several options. The options that will use are onSuccess, onError, and onSettled. onSuccess will be triggered every time a mutation is a success, onError will be triggered when there is an error during a mutation and onSettled will be triggered either when the mutation is a success or not.

const { mutate, isLoading } = useMutation(createEmployee, {
   onSuccess: data => {
      console.log(data);
      const message = "success"
      alert(message)
},
  onError: () => {
       alert("there was an error")
},
  onSettled: () => {
     queryClient.invalidateQueries('create')
}
});

After the above steps have been completed, your emloyeeForm component will look like the code snippet below.

/* eslint-disable react/jsx-wrap-multilines */
import React from 'react';
import { useMutation, useQueryClient } from 'react-query';
import { useForm } from 'react-hook-form';
import PropTypes from 'prop-types';

import {
  Button,
  TextField,
  Dialog,
  DialogContent,
  DialogActions,
  DialogContentText,
  CircularProgress
} from '@material-ui/core';
import DialogTitle from '@material-ui/core/DialogTitle';
import axios from '../clientProvider/axiosConfig';

const createEmployee = async (data: Employee) => {
  const { data: response } = await axios.post('https://employee.free.beeceptor.com/create', data);
  return response.data;
};

interface Employee {
  name: string;
  job: string;
  id: string;
};

const EmployeeForm = props => {
  const { open, handleClose } = props;
  const queryClient = useQueryClient();
  const { register, handleSubmit, errors } = useForm<Employee>({
    mode: 'onChange'
  });
  const { mutate, isLoading } = useMutation(createEmployee, {
    onSuccess: data => {
      console.log(data);
      const message = "success"
      alert(message)
    },
    onError: () => {
      alert("there was an error")
    },
    onSettled: () => {
      queryClient.invalidateQueries('create');
    }
  });
  const onSubmit = (data: Employee) => {
    const employee = {
      ...data
    };
    mutate(employee);
  };

  return (
    <div>
      <Dialog
        aria-labelledby="form-dialog-title"
        onClose={handleClose}
        open={open}
      >
        <form onSubmit={handleSubmit(onSubmit)}>
          <DialogTitle color="primary" id="form-dialog-title">
            Employee
          </DialogTitle>
          <DialogContent>
            <DialogContentText>Add employee</DialogContentText>

            <TextField
              error={Boolean(errors.name)}
              inputRef={register({ required: true })}
              fullWidth
              id="name"
              label="Name"
              margin="dense"
              name="name"
              type="text"
              variant="outlined"
            />

            <TextField
              error={Boolean(errors.job)}
              inputRef={register({ required: true })}
              fullWidth
              id="job"
              label="Job"
              margin="dense"
              name="job"
              type="text"
              variant="outlined"
            />

            <TextField
              error={Boolean(errors.id)}
              inputRef={register({
                required: true
              })}
              fullWidth
              id="id"
              label="ID"
              margin="dense"
              name="id"
              type="text"
              variant="outlined"
            />

          </DialogContent>
          <DialogActions>
            <Button color="primary" onClick={handleClose} variant="outlined">
              Cancel
            </Button>
            <Button
              color="primary"
              type="submit"
              variant="contained"
              disabled={isLoading}
              startIcon={
                isLoading ? (
                  <CircularProgress color="inherit" size={25} />
                ) : null
              }
            >
              Create
            </Button>
          </DialogActions>
        </form>
      </Dialog>
    </div>
  );
};

EmployeeForm.propTypes = {
  handleClose: PropTypes.func,
  open: PropTypes.bool
};
// }
export default EmployeeForm;

Fetching data with axios

Create a functional component called employeeList that will be fetching and list data. Fetching is simple and straight forward, react-query provides a hook called useQuery which takes a key and function responsible for fetching data. Import the hook called useQuery from react-query.

import { useQuery } from 'react-query';

Create a function responsible for fetching data from a dummy rest api called restapiexample. Learn about restapiexample.

const getEmployee = async () => {
const { data } = await axios.get('http://dummy.restapiexample.com');
return data.data;
};

Destructure data which is returned by the hook with several other returns available from the useQuery hook. useQuery provides several options to choose from. Learn about useQuery options. .

const { data } = useQuery('create', getEmployee);

The complete employeeList should look like the snippet below.

import React from 'react';
import MUIDataTable from 'mui-datatables';
import { useQuery } from 'react-query';
import EmployeeForm from './employeeForm'
import ToolBar from './ToolBar';
import axios from '../clientProvider/axiosConfig';

const getEmployee = async () => {
  const { data } = await axios.get('/employees');
  return data.data;
};
const EmployeeList: React.FC = () => {
  const { data } = useQuery('create', getEmployee);
  return (
    <div style={{ padding: '50px', marginLeft: '250px' }}>
      <ToolBar buttonText="Employee" formDialog={EmployeeForm} />
      <MUIDataTable
        columns={[
          {
            name: '_id',
            label: 'id',
            options: {
              display: 'false',
              filter: false
            }
          },
          {
            name: 'employee_name',
            label: 'Employee Name',
            options: {
              filter: true,
              sort: true
            }
          },
          {
            name: "employee_salary",
            label: 'Employee Salary',
            options: {
              filter: true,
              sort: false,
              customBodyRender: (value, tableMeta, updateValue) => {
                const nf = new Intl.NumberFormat("en-US", {
                  style: "currency",
                  currency: "ZMW",
                  minimumFractionDigits: 2,
                  maximumFractionDigits: 2
                });
                return nf.format(value);
              }
            }
          },
          {
            name: 'employee_age',
            label: 'Age',
            options: {
              filter: true,
              sort: false
            }
          },
        ]}
        data={data}
        options={{
          filter: true,
          viewColumns: false,
          selectableRows: 'single',
          selectableRowsOnClick: true,
          elevation: 0,
          rowsPerPage: 10,
          responsive: 'simple',
          filterType: 'dropdown',
        }}
        title=""
      />
    </div>
  );
};

export default EmployeeList;

Check out the git repo for the other configurations that we did not focus on but appear in the article. Feel free to ask me any questions on twitter , am available to help you get started.