Search code examples
reactjsreact-hooksreact-reduxrtk-query

Why RTK Query dont realize auto-refetch?


I have the following issue. I am in a local development which I am implementing RTK Query to make requests to my API.

All good, but the problem I'm having is when I want to implement auto-refetch with the providerTags.

If I work the code in the following way everything works perfect:


// todoApi.ts


import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { Todo } from "../../models";
import { TodoCreateDTO } from "../../models/dto";
import { TodoUpdateDTO } from "../../models/dto/TodoUpdate.dto";

export const todoApi = createApi({
    reducerPath: "api",
    baseQuery: fetchBaseQuery({
        baseUrl: "http://localhost:3000/",
        headers: {
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Methods": "OPTIONS, POST, GET, PUT, PATCH",
        },
        credentials: "include",
    }),
    tagTypes: ['Todos'],
    endpoints: (builder) => ({
        todos: builder.query<Todo[], void>({
            query: () => ({
                url: 'todos'
            }),
            providesTags: ['Todos'],
        }),
        todo: builder.query<Todo, number>({
            query: (id) => ({
                url: `todos/${id}`
            }),
            providesTags: ['Todos']
        }),
        createTodo: builder.mutation<Todo, TodoCreateDTO>({
            query: (todo) => ({
                url: 'todos',
                method: "POST",
                body: todo
            }),
            invalidatesTags: ['Todos']
        }),
        updateTodo: builder.mutation<Todo, TodoUpdateDTO>({
            query: ({ id, ...todo }) => ({
                url: `todos/${id}`,
                method: "PATCH",
                body: todo
            }),
            invalidatesTags: ['Todos']
        }),
        deleteTodo: builder.mutation<Todo, number>({
            query: (id) => ({
                url: `todos/${id}`,
                method: "DELETE"
            }),
            invalidatesTags: ['Todos']
        }),
    }),
});


export const {
    useTodosQuery,
    useTodoQuery,
    useCreateTodoMutation,
    useUpdateTodoMutation,
    useDeleteTodoMutation
} = todoApi




// Todos.tsx

import { useTodosQuery } from "../../app/services/todoApi"
import { TodoCard } from "./TodoCard";

export interface TodosProps {

}

export const Todos = () => {

    const { data, error, isLoading, isFetching, isSuccess } = useTodosQuery();

    return (
        <>
            {isLoading && <h2>... Loading</h2>}
            {isFetching && <h2>... Fetching</h2>}
            {error && <h2>Error</h2>}
            {isSuccess && (
                <>
                    {data.map(todo => (
                        <TodoCard
                            id={todo.id} />
                    ))}
                </>
            )}
        </>
    )
}

// TodoCard

import { MButton, MCards } from "@inversiones-ma/finfast-react-controls";
import { useTodoQuery } from "../../app/services/todoApi";

export interface TodoCardProps {
    id: number
};

export const TodoCard = ({ id }: TodoCardProps) => {

    const { data } = useTodoQuery(id);

    return (
        <MCards className='p-4 mb-1' color="warning">
            <h5 className="mb-0">{data?.title}</h5>
            <p className="mb-0">{data?.description}</p>
            <small className="text-muted">{data?.status ? "Finalizada" : "Pendiente"}</small>
            <div>
                <div className="btn-group">
                    <MButton color="success">Update</MButton>
                    <MButton color="error">Delete</MButton>
                </div>
            </div>
        </MCards>
    )
};


// CreateTodo

import { useForm, SubmitHandler } from "react-hook-form";
import { MButton, MCards, MInputGeneric, MSwitch } from "@inversiones-ma/finfast-react-controls";
import { useCreateTodoMutation, useTodosQuery } from "../../app/services/todoApi";
import { TodoCreateDTO, UserCreateDTO } from "../../models/dto";

type InputCreateTodo = {
    title: string,
    description: string,
    status: boolean
};

export interface CreateTodoProps {

};

const user: UserCreateDTO = {
    email: "[email protected]",
    name: "My Name",
    password: "mySecretPassword",
    username: "myUsername"
};

export const CreateTodo = () => {

    const [createTodo] = useCreateTodoMutation();
    const {refetch} = useTodosQuery();

    const form = useForm<InputCreateTodo>();
    const { handleSubmit } = form;

    const onSubmit: SubmitHandler<InputCreateTodo> = async (data) => {

        let todo: TodoCreateDTO = {
            title: data.title,
            description: data.description,
            status: data.status,
            user
        };

        await createTodo(todo);
        refetch()
    }

    return (
        <MCards className='p-4'>
            <h5>Create new todo</h5>
            <form onSubmit={handleSubmit(onSubmit)}>
                <MInputGeneric form={form} name={"title"} label={"Title"} ></MInputGeneric>
                <MInputGeneric form={form} name={"description"} label={"Description"} ></MInputGeneric>
                <div className="d-flex align-items-center">
                    <small className="mx-2">Finalizada</small>
                    <MSwitch form={form} name={"status"}></MSwitch>
                </div>
                <MButton type="submit" size="mini" className="w-100 mt-3">Create</MButton>
            </form>
        </MCards>
    )
}

As I understand it, every time I add a providerTag to a query, by means of invalideTag to a mutation I invalidate the call and the autorefetch should be performed. But this doesn't happen, I've thought about it a thousand times but I can't find the solution, everything is fixed if I make the call manually in the CreateTodo.tsx component after calling the mutation by doing the refetch().

Does anyone see something that is happening to me? I would be very grateful.


Solution

  • With the help of @phry's comment I was able to find the solution, it turns out that in the Redux store I did not instantiate the todoAPI middleware. This was resolved by adding the following line:

    import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
    import { todoApi } from './services/todoApi';
    
    export const store = configureStore({
      reducer: {
        [todoApi.reducerPath]: todoApi.reducer
      },
      middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(todoApi.middleware)
    });
    
    export type AppDispatch = typeof store.dispatch;
    export type RootState = ReturnType<typeof store.getState>;
    export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, RootState, unknown, Action<string>>;
    
    

    Thanks!