Reputation: 1031
Hi I am new to RTK Query and Redux toolkit. I am working on a pet project where I am trying to poll a workflow status API until it gives me a workflow status as 'CLOSED'
Now I am not sure how to poll this API within my component since I would have done that within a useEffect
but react doesn't allow hooks within hooks so I can not use my useAPIQuery
RTK-Query hooks within useEffect.
const getBaseQuery = async (apiPromise: Promise<ServiceModel>) => {
try {
const response = await apiPromise;
if (response) {
return {
data: response,
};
}
throw new Error('No data received from service');
} catch (err) {
return {
error: err,
};
}
};
export const api = createApi({
reducerPath: 'api',
baseQuery: getBaseQuery,
endpoints: (builder) => ({
runWorfklow: builder.query({
query: ({ buildingId, deviceIds }) => getRunWorkflowPromise({ buildingId, deviceIds }),
transformResponse: (response: RunWorkflowOutput) => ({ ...response }),
}),
getWorkflowStatus: builder.query({
query: ({ buildingId, runId }) => getWorkflowStatusPromise({ buildingId, runId }),
transformResponse: (response: GetWorkflowStatusOutput) => ({ ...response }),
}),
}),
});
// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useRunWorfklowQuery, useGetWorkflowStatusQuery } = api;
const LoaderBox = (props: LoaderBoxProps) => {
const { buildingId, devicesId } = props;
const {
data: runWorkflowData,
error: runWorkflowError,
isLoading: isRunWorkflowLoading,
isSuccess: isRunWorkflowSuccess,
} = useRunWorfklowQuery({
buildingId,
devicesId,
});
// This call works perfectly fine and give me runStatusData.runStatus as 'OPEN', showing it here for demonstrating how the API would work.
const {
data: runStatusData,
error: runStatusError,
isLoading: runStatusLoading,
isUninitialized: runStatusUninitialized,
} = useGetWorkflowStatusQuery(
isRunWorkflowSuccess ? { buildingId, runId: runWorkflowData.runId } : skipToken,
);
const [isRunFinished, setIsRunFinished] = useState(false);
useEffect(() => {
// To continuously poll for WF status
const poll = () => {
const { data } = useGetWorkflowStatusQuery(
isRunWorkflowSuccess ? { buildingId, runId: runWorkflowData.runId } : skipToken,
);
if (data?.runStatus === 'CLOSED') {
setIsRunFinished(true);
} else {
setTimeout(() => {
poll();
}, 5000);
}
};
poll();
}, [isRunWorkflowSuccess, runWorkflowData, buildingId]);
return (
<>
{!runWorkflowError ? (
<>
<Text> Requested workflow run</Text>
isRunFinished ? <Text>Workflow finished</Text> : <Text>Workflow in progress</Text>
</>
)
: <></>}
{}
</>
);
Now I can make it work by not using the useGetWorkflowStatus
hook and directly using the getWorkflowStatusPromise
in my useEffect
though I wanted to see if I can still use the RTK query hooks to get this done.
The other thing I tried was to add retry on the endpoints
in my apiSlice, but there I am not able to do that because my baseQuery function doesn't accept any retry and if I try to add a retry param to my baseQuery function, createAPI doesn't allow that.
The other thing I tried was to use lazyQuery for initiating the GetWorfklowStatus calls after the runWorkflow call gets complete but that did not work either, it fails with an error - This expression is not callable. Type '[LazyQueryTrigger<QueryDefinition<any, (apiPromise: Promise<ServiceModel>) => Promise<{ data: ServiceModel; error?: undefined; } | { error: unknown; data?: undefined; }>, never, { ...; }, "api">>, UseQueryStateDefaultResult<...>, UseLazyQueryLastPromiseInfo<...>]' has no call signatures.
I'll appreciate any pointers which redirect me into the right direction and happy to provide more information on this if needed.
Thanks
Upvotes: 5
Views: 5425
Reputation: 203208
I think you are close with the skipToken
approach. The query hooks already have a polling capability built-in. I'd suggest something close to the following:
const runStatusDataRef = React.useRef();
const {
data: runStatusData,
error: runStatusError,
isLoading: runStatusLoading,
isUninitialized: runStatusUninitialized,
} = useGetWorkflowStatusQuery(
{ buildingId, runId: runWorkflowData.runId },
{
pollingInterval: 5000,
skip: !isRunWorkflowSuccess || runStatusDataRef.current?.runStatus === 'CLOSED',
}
);
React.useEffect(() => {
runStatusDataRef.current = runStatusData;
}, [runStatusData]);
If you need to use skipToken
you might get by with just setting the polling interval and passing the additional condition to the ternary expression:
const runStatusDataRef = React.useRef();
const {
data: runStatusData,
error: runStatusError,
isLoading: runStatusLoading,
isUninitialized: runStatusUninitialized,
} = useGetWorkflowStatusQuery(
isRunWorkflowSuccess && runStatusDataRef.current?.runStatus !== 'CLOSED'
? { buildingId, runId: runWorkflowData.runId }
: skipToken,
{ pollingInterval: 5000 },
);
React.useEffect(() => {
runStatusDataRef.current = runStatusData;
}, [runStatusData]);
Upvotes: 2