Search code examples
javascriptreactjsreduxredux-toolkitrtk-query

fetchInvoicesByOffice is not a function RTK QUERY


I'm having an issue with calling an auto created hook by Redux-Toolkit (RTK) Query (RTKQ). The issue I get is the following: "fetchInvoicesByOffice is not a function"

Store

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

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

Slice

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

export const InvoiceSlice = createApi({
  reducerPath: "InvoiceSlice",
  baseQuery: fetchBaseQuery({
    baseUrl: "http://INVOICES_API:8081/api/",
  }),
  tagTypes: ["Invoices"],
  endpoints: (builder) => ({
    fetchInvoices: builder.query({
      query: () => "GetAllInvoices",
      providesTags: ["Invoices"],
    }),
    fetchInvoice: builder.query({
      query: (id) => `GetInvoice/${id}`,
      providesTags: ["Invoices"],
    }),
    fetchInvoicesByOffice: builder.query({
      query: (id) => `GetAllInvoices_ByOffice/${id}`,
      providesTags: ["Invoices"],
    }),
    addInvoiceComment: builder.mutation({
      query: (data) => ({
        url: "addComment",
        method: "POST",
        body: data,
      }),
      invalidatesTags: ["Invoices"],
    }),
    updateInvoicesStatus: builder.mutation({
      query: (data) => ({
        url: "values",
        method: "PUT",
        body: data,
      }),
      invalidatesTags: ["Invoices"],
    }),
    AddMatter: builder.mutation({
      query: (data) => ({
        url: "UpdateMatter",
        method: "PUT",
        body: data,
      }),
      invalidatesTags: ["Invoices"],
    }),
    fetchInvoice: builder.query({
      query: (id) => `GetComment/${id}`,
      providesTags: ["Invoices"],
    }),
  }),
});

export const {
  useFetchInvoicesQuery,
  useFetchInvoiceQuery,
  useFetchInvoicesByOfficeQuery,
  useAddInvoiceCommentMutation,
  useUpdateInvoicesStatusMutation,
} = InvoiceSlice;

Component

import React, { useState, useEffect } from "react";
import "./InvoiceList";
import "./InvoiceList.css";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import { css } from "glamor";
import {
  useFetchInvoicesQuery,
  useLazyFetchInvoicesByOfficeQuery,
} from "../../features/Invoices/InvoiceSlice";

const InvoiceList = () => {
  const { data, refetch } = useFetchInvoicesQuery();
  const [fetchInvoicesByOffice, { currentData }] =
    useLazyFetchInvoicesByOfficeQuery();
  const [checked, setChecked] = useState(false);
  const [rowClicked, setRowClicked] = useState(0);
  const [office, setOffice] = useState("ALL");
  const [selection, setSelection] = useState("ALL");
  const [invoiceStatus, setInvoiceStatus] = useState();
  const [matter, setMatter] = useState("");
  const loggedUser = JSON.parse(localStorage.getItem("user"));
  const [invoicesState, setInvoicesState] = useState([]);
  const [term, setTerm] = useState("");
  const [userid, setUserId] = useState("");

  useEffect(() => {
    setUserId(loggedUser);
    setInvoicesState(data);
  }, [data]);

  let toastId = null;
  const displayToast = (message) => {
    if (!toast.isActive(toastId)) {
      toastId = toast(`${message} !!! 🚀`, {
        className: css({
          background: "#9e007e !important",
          color: "white !important",
          fontWeight: "bold",
        }),
        autoClose: 1000,
        closeOnClick: true,
        toastId: "my_toast",
        autoClose: true,
        closeButton: false,
        position: toast.POSITION.BOTTOM_CENTER,
      });
    }
  };

  const onSearchSubmit = (term) => {
    setTerm(term);
  };

  const handleOfficeClick = async (offc) => {
    setOffice(offc);

    if (office === "ALL") {
      refetch();
      setInvoicesState(data);
    } else {
      setOffice((prevState) => {
        fetchInvoicesByOffice(prevState);
        setInvoicesState(currentData);

        return !prevState;
      });
      // await fetchInvoicesByOffice(offc);
      // await setInvoicesState(currentData);
    }
  };

  useEffect(() => {
    if (office === "ALL") {
      setOffice(office);
      fetchInvoicesByOffice("ALL");
    } else {
      setOffice(office);
      fetchInvoicesByOffice(office);
    }
  }, [office]);

I want to be able to use the hook in either the useEffect hook callback or the handleOfficeClick handler...........................................................


Solution

  • The RTKQ query hook itself is what calls the endpoint and its return value is the fetched data and associated metadata.

    See useQuery for more details. Here's a query hook's function signature.

    type UseQuery = (
      arg: any | SkipToken,
      options?: UseQueryOptions
    ) => UseQueryResult
    
    type UseQueryOptions = {
      pollingInterval?: number
      refetchOnReconnect?: boolean
      refetchOnFocus?: boolean
      skip?: boolean
      refetchOnMountOrArgChange?: boolean | number
      selectFromResult?: (result: UseQueryStateDefaultResult) => any
    }
    
    type UseQueryResult<T> = {
      // Base query state
      originalArgs?: unknown // Arguments passed to the query
      data?: T // The latest returned result regardless of hook arg, if present
      currentData?: T // The latest returned result for the current hook arg, if present
      error?: unknown // Error result if present
      requestId?: string // A string generated by RTK Query
      endpointName?: string // The name of the given endpoint for the query
      startedTimeStamp?: number // Timestamp for when the query was initiated
      fulfilledTimeStamp?: number // Timestamp for when the query was completed
    
      // Derived request status booleans
      isUninitialized: boolean // Query has not started yet.
      isLoading: boolean // Query is currently loading for the first time. No data yet.
      isFetching: boolean // Query is currently fetching, but might have data from an earlier request.
      isSuccess: boolean // Query has data from a successful load.
      isError: boolean // Query is currently in an "error" state.
    
      refetch: () => void // A function to force refetch the query
    }
    

    If you want wanting to intentionally trigger a query/fetch in a callback then you are looking for the lazy queries. These return an array that includes a "trigger function".

    const [trigger, result, lastPromiseInfo] =
      api.endpoints.getPosts.useLazyQuery(options);
    

    From your API slice export the generated

    export const {
      useFetchInvoicesQuery,
      useFetchInvoiceQuery,
      useFetchInvoicesByOfficeQuery,
      useLazyFetchInvoicesByOfficeQuery, // <-- lazy query
      useAddInvoiceCommentMutation,
      useUpdateInvoicesStatusMutation,
    } = InvoiceSlice;
    

    Import the lazy query hook in the UI and use the returned trigger function.

    ...
    import {
     ...,
     useLazyFetchInvoicesByOfficeQuery,
    } from "../../features/Invoices/InvoiceSlice";
    
    const InvoiceList = () => {
      const { data, refetch } = useFetchInvoicesQuery();
      const [fetchInvoicesByOffice] = useLazyFetchInvoicesByOfficeQuery();
    
      const handleOfficeClick = async (offc) => {
        await setChecked((old) => !old);
        await setOffice(offc);
        await fetchInvoicesByOffice(office);
      };
    
      useEffect(() => {
        fetchInvoicesByOffice(office);
        if (office === "ALL") refetch();
      }, [office]);
    
      ...