DEV Community

Cover image for Conditional Fetching - Redux-Toolkit Query (RTK Query)
Kunal Ukey
Kunal Ukey

Posted on

Conditional Fetching - Redux-Toolkit Query (RTK Query)

Introduction: RTK Query is a powerful data fetching and caching tool. It is designed to simplify common cases for loading data in a web application, eliminating the need to hand-write data fetching & caching logic yourself.

Problem: Query hooks automatically begin fetching data as soon as the component is mounted. But, there are use cases where you may want to delay fetching data until some condition becomes true.

Solution:

  • Using skip parameter in a hook
  • Using Lazy Query hook

Requirements:

  • Basic React Knowledge
  • Basic Redux-Toolkit Knowledge

Project Setup

We'll start by installing redux-toolkit & react-redux library, so open a terminal and run the command below.

npm install @reduxjs/toolkit react-redux 
Enter fullscreen mode Exit fullscreen mode

Now, Create a folder named redux inside the src folder.
Inside this redux folder, We'll create our api.js & store.js files.

api.js

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

// Generates react hooks for endpoints
export const api = createApi({
    reducerPath: "api",
    baseQuery: fetchBaseQuery({baseUrl: "https://jsonplaceholder.typicode.com/"}),
    endpoints: (builder) => ({
        getUsers: builder.query({
            query: (user) => `users/${user}`
        }),
    })
});

// Export endpoints as hooks
export const { useGetUsersQuery } = api;
Enter fullscreen mode Exit fullscreen mode

store.js

import { configureStore } from "@reduxjs/toolkit";
import { api } from "./api";

// Combines multiple states as root state
export const store = configureStore({
    reducer: {
        [api.reducerPath]: api.reducer,
    },
    // getDefaultMiddleware enables important feature like caching.
    middleware: (getDefaultMiddleware) => {
        return getDefaultMiddleware().concat(api.middleware)
    }
});
Enter fullscreen mode Exit fullscreen mode

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { Provider } from "react-redux";
import { store } from "./redux/store";

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    // Wrap root component with Provider
    // Provider accepts store (root state) as prop
    <Provider store={store}> 
      <App />
    </Provider>
  </React.StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

Solution 1: Using skip parameter

App.js

import { useEffect, useState } from "react";
import { useGetUsersQuery } from "./redux/api";

function App() {
  const [userData, setUserData] = useState();
  const [isUser, setIsUser] = useState(true);
  // Pass skip parameter that accepts a boolean
  const { data } = useGetUsersQuery(1, { skip: isUser });

  useEffect(() => {
    if(data) {
      setUserData([data]);
    }
    console.log(data)
  },[data])

  return (
    <div className="App"> 
      {userData && userData.map(item => (
        <div key={item.id}> 
          <p>{item.name}</p> 
          <p>{item.email}</p> 
        </div> 
      ))}
      // Triggers fetch as skip parameter is false
      <button onClick={() => setIsUser(false)}>Fetch User</button> 
    </div> 
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Solution 2: Using Lazy Query hook

For using a Lazy Query hook, we have to make a minor change to the exports inside the api.js file.

api.js

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

export const api = createApi({
    reducerPath: "api",
    baseQuery: fetchBaseQuery({baseUrl: "https://jsonplaceholder.typicode.com/"}),
    endpoints: (builder) => ({
        getUsers: builder.query({
            query: (user) => `users/${user}`
        }),
    })
});

// Add Lazy after "use" to convert it into Lazy Query hook
export const { useLazyGetUsersQuery } = api;

Enter fullscreen mode Exit fullscreen mode

App.js

import { useEffect, useState } from "react";
import { useLazyGetUsersQuery } from "./redux/api";

function App() {
  const [userData, setUserData] = useState();
  // Returns trigger function and results object
  const [getUsers, results] = useLazyGetUsersQuery();

  useEffect(() => {
    if(results && results.data) {
      setUserData([results.data]);
    }
    console.log(results)
  },[results])

  return (
    <div className="App">
      {userData && userData.map(item => (
        <div key={item.id}>
          <p>{item.name}</p>
          <p>{item.email}</p>
        </div>
      ))}
      // Fetch data on button click by envoking trigger function
      <button onClick={() => getUsers(1)}>Fetch User</button>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Video Tutorial:

Thanks for reading! ❤

Oldest comments (3)

Collapse
 
eduardoklein profile image
Eduardo Klein

Just curious, why you're creating a local state to store the data returned from the hook? You can access this data directly from both useQuery (with skip) and useLazyQuery

Collapse
 
davidko5 profile image
Davyd Kondratenko • Edited

maybe to have UI updating in response to data fetching ?

Collapse
 
turagoda profile image
Thishan Uragoda

You can't mutate the result in the useQuery directly. Try using map function to mutate the data to different structure and it'll give an error (read only data).