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)
}