Skip to content

suyogmb/RTK-Query-Implementation

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

64 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RTK Query vs. Traditional Redux Data Fetching (Quick Reference)

This file shows the same “list + add item” flow implemented two ways: traditional Redux with manual thunks/reducers, and RTK Query with generated hooks. Use this to explain why RTK Query reduces boilerplate and adds caching.

Traditional (manual thunk + reducer)

// store/traditional/posts.ts
import {createSlice, createAsyncThunk} from '@reduxjs/toolkit';
import axios from 'axios';

export const fetchPosts = createAsyncThunk('posts/fetch', async () => {
  const {data} = await axios.get('https://jsonplaceholder.typicode.com/posts');
  return data;
});

export const addPost = createAsyncThunk('posts/add', async (body) => {
  const {data} = await axios.post('https://jsonplaceholder.typicode.com/posts', body);
  return data;
});

const postsSlice = createSlice({
  name: 'posts',
  initialState: {items: [], loading: false, error: null as string | null},
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchPosts.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchPosts.fulfilled, (state, action) => {
        state.loading = false;
        state.items = action.payload;
      })
      .addCase(fetchPosts.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message ?? 'Error';
      })
      .addCase(addPost.fulfilled, (state, action) => {
        // manual list update
        state.items.unshift(action.payload);
      });
  },
});

export default postsSlice.reducer;
// components/PostsTraditional.tsx
import React, {useEffect, useState} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {addPost, fetchPosts} from '../store/traditional/posts';
import {RootState} from '../store';

export default function PostsTraditional() {
  const dispatch = useDispatch();
  const {items, loading, error} = useSelector((s: RootState) => s.posts);
  const [title, setTitle] = useState('');

  useEffect(() => {
    dispatch(fetchPosts());
  }, [dispatch]);

  const handleAdd = () => dispatch(addPost({title, body: 'demo', userId: 1}));

  if (loading) return <Text>Loading…</Text>;
  if (error) return <Text>{error}</Text>;

  return (
    <>
      <Button title="Add" onPress={handleAdd} />
      {items.map((p) => (
        <Text key={p.id}>{p.title}</Text>
      ))}
    </>
  );
}

Characteristics

  • Boilerplate: multiple thunks, reducers, action types, selectors.
  • Caching/deduping: none by default.
  • Refetch after mutation: must be wired manually (e.g., dispatch fetch again).
  • Loading/error flags: hand-rolled.

RTK Query (generated hooks + cache)

// services/api.ts (excerpt)
import {createApi, fetchBaseQuery} from '@reduxjs/toolkit/query/react';

export const api = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({baseUrl: 'https://jsonplaceholder.typicode.com'}),
  tagTypes: ['Posts'],
  endpoints: (builder) => ({
    getPosts: builder.query<any[], void>({
      query: () => '/posts',
      providesTags: ['Posts'],
    }),
    addPost: builder.mutation<any, Partial<any>>({
      query: (body) => ({url: '/posts', method: 'POST', body}),
      invalidatesTags: ['Posts'], // triggers refetch of getPosts
    }),
  }),
});

export const {useGetPostsQuery, useAddPostMutation} = api;
// components/PostsRTKQuery.tsx
import React, {useState} from 'react';
import {useGetPostsQuery, useAddPostMutation, api} from '../services/api';
import {useDispatch} from 'react-redux';

export default function PostsRTKQuery() {
  const dispatch = useDispatch();
  const {data: posts, isLoading, isError, refetch} = useGetPostsQuery();
  const [addPost, {isLoading: isAdding}] = useAddPostMutation();
  const [title, setTitle] = useState('');

  const handleAdd = async () => {
    const res = await addPost({title, body: 'demo', userId: 1}).unwrap();
    // optimistic insert (useful for mock APIs that don’t persist)
    dispatch(
      api.util.updateQueryData('getPosts', undefined, (draft) => {
        draft.unshift({id: res?.id ?? Math.random().toString(), title, body: 'demo', userId: 1});
      }),
    );
  };

  if (isLoading) return <Text>Loading…</Text>;
  if (isError) return <Text>Error</Text>;

  return (
    <>
      <Button title={isAdding ? 'Adding…' : 'Add'} onPress={handleAdd} disabled={isAdding} />
      <Button title="Refetch" onPress={refetch} />
      {posts?.map((p) => (
        <Text key={p.id}>{p.title}</Text>
      ))}
    </>
  );
}

Characteristics

  • Minimal boilerplate: endpoints + generated hooks.
  • Built-in cache/deduping: no duplicate requests for the same query args.
  • Auto-refetch: invalidatesTags refreshes lists after mutations.
  • Loading/error: provided flags (isLoading, isError, isFetching, isSuccess).
  • Extras out-of-the-box: polling, skip/conditional queries, prefetch, optimistic updates.

Quick selling points for the team

  • Less code, fewer bugs: No hand-written request/success/failure reducers.
  • Consistency: One API layer defines base URL, headers, retries, and tags.
  • Performance: Cached responses and deduped requests reduce network churn.
  • DX: Generated hooks with types; Redux DevTools support.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 7