Home > Blockchain >  React Hook cannot be called inside a callback
React Hook cannot be called inside a callback

Time:05-03

Problem Statement :

I am trying to setup a react component that will make an API call whenever a value is selected from the select box. I tried to make that happen in the useEffect hook but I am getting errors based on the rule of hooks that we can not call any hook inside a callback. Can you please tell me how can I fix this issue and do the required API call on any of the user Input.

I am looking over the pointers that can help me prevent this error and at the same time make an API call to the backend to fetch the records

Here is my code :

Component


const component: React.FC<ComponentProps> = () => {
  const { user } = useAppSelector((state) => state.auth);
  const periods = getPeriodNames();

  const [selectedPeriod, setSelectedPeriod] = React.useState(periods[0]);
  const [records, setRecords] = React.useState([]);
  const [columns, setColumns] = React.useState<any>();

  React.useEffect(() => {
    const [request] = React.useState<Request>({ // Throwing error:  React Hook "React.useState" cannot be called inside a callback.
      requester: user.alias,
      accountingMonth: selectedPeriod,
      limit: 300,
    });
    const { data, error, isLoading, isSuccess, isError } =
      useQuery(request); // Throwing error : React Hook "useQuery" cannot be called inside a callback.
    setRecords(data?.value);
  }, [selectedPeriod, user.alias]);

  const onPeriodSelect = (detail: SelectProps.ChangeDetail) => {
    setSelectedPeriod(selectedOption);
  };

  React.useEffect(() => {
    if (records) {
      // do something
    }
  }, [records]);

  return (
    <>
      <Select
        selectedOption={selectedPeriod}
        onChange={({ detail }) => onPeriodSelect(detail)}
        options={periods}
        selectedAriaLabel="Selected"
      />
    </>
  );
};

Setup to make an API Call


export const dynamicBaseQuery: BaseQueryFn<
  string | FetchArgs,
  unknown,
  FetchBaseQueryError
> = async (args, api, extraOptions) => {
  const { mainApiUrl } = (api.getState() as RootState).settings.endpoints;
  const rawBaseQuery = fetchBaseQuery({
    baseUrl: mainApiUrl,
    prepareHeaders: (headers, { getState }) => {
      // Use getState to pull the jwtToken and pass it in the headers to the api endpoint.
      const { jwtToken } = (getState() as RootState).auth;
      headers.set("authorization", jwtToken);

      return headers;
    },
  });
  return rawBaseQuery(args, api, extraOptions);
};

export const mainApi = createApi({
  reducerPath: "mainApi",
  baseQuery: dynamicBaseQuery,
  endpoints: () => ({}),
});

const useQuery = mainApi.injectEndpoints({
  endpoints: (builder) => ({
    query: builder.query<response, request>({
      query: (request?: request) => ({
        url: "/test_url",
        body: request,
        method: "POST",
      }),
    }),
  }),
  overrideExisting: false,
});

Any help would be really appreciated. Thanks

CodePudding user response:

As the error tells, you should move your custom hook useQuery out of useEffect

You can add it on top of your component instead like below

const component: React.FC<ComponentProps> = () => {
  const { user } = useAppSelector((state) => state.auth);
  const [request, setRequest] = React.useState<Request | undefined>();
  const periods = getPeriodNames();
  const { data, error, isLoading, isSuccess, isError } =
      useQuery(request); //when component get re-rendered, and request state is there, it will fetch data


  const [selectedPeriod, setSelectedPeriod] = React.useState(periods[0]);
  const [records, setRecords] = React.useState([]);
  const [columns, setColumns] = React.useState<any>();

  //fetched successfully
  React.useEffect(() => {
    if(data) {
       setRecords(data.value);
    }
  }, [data])

  React.useEffect(() => {
    setRequest({
      requester: user.alias,
      accountingMonth: selectedPeriod,
      limit: 300,
    })
  }, [selectedPeriod, user.alias]);

  const onPeriodSelect = (detail: SelectProps.ChangeDetail) => {
    setSelectedPeriod(selectedOption);
  };

  React.useEffect(() => {
    if (records) {
      // do something
    }
  }, [records]);

  return (
    <>
      <Select
        selectedOption={selectedPeriod}
        onChange={({ detail }) => onPeriodSelect(detail)}
        options={periods}
        selectedAriaLabel="Selected"
      />
    </>
  );
};

CodePudding user response:

You can put your API call inside a callback and call it inside your selectbox handler.

example:

const apiCall = (item) => {
  // api call logic
}

const handleSelectBox = (selectedItem)=> {
  apiCall(selectedItem)
}
  • Related