redux-toolkit icon indicating copy to clipboard operation
redux-toolkit copied to clipboard

Feature request: pass `pollingInterval` a function

Open harry-gocity opened this issue 1 year ago • 1 comments

Use case:

  • I have an API where I need to place an order with one endpoint and then poll for the order to get the payment status with another endpoint.
  • Depending on the payment status, I move the customer on to a confirmation page or let them retry their payment details.
  • I want to stop polling when the payment status is FAILED. Currently my component looks a bit like this

Here's a rough WIP of how I would see this looking in practice:

export const PaymentForm = (): ReactElement => {
    const [shouldPoll, setShouldPoll] = useState(false);

    const [createOrder, { data: createdOrder }] = useCreateOrderMutation();

    const { data: orderStatus, isSuccess: hasOrderStatus } = useGetOrderStatusQuery(
        createdOrder ? { orderNumber: createdOrder.orderNumber } : skipToken,
        { pollingInterval: shouldPoll ? 1000 : 0 }
    );

    if (shouldPoll && hasOrderStatus && orderStatus.paymentStatus === 'FAILED') {
        setShouldPoll(false);
    }

    if (orderStatus.paymentStatus === 'SUCCESS') {
        // navigate to confirmation page
    }

    const handleSubmit = () => {
        createOrder()
            .unwrap()
            .then(() => {
                setShouldPoll(true);
            })
            .catch(() => {
                setShouldPoll(false);
            });
    };

    return (
        <div>
            <form onSubmit={handleSubmit} />
            {/* Something using orderStatus */}
        </div>
    );
};

I'd like to be able to eliminate the need for the shouldPoll status, which I'm currently updating after unwrapping createOrder and comparing/updating in render.

If pollingInterval accepted a callback that received the current query data (and possibly other flags like isSuccess) I could use the current state of the data to determine if the query should keep polling:

const { data: orderStatus, isSuccess: hasOrderStatus } = useGetOrderStatusQuery(
    createdOrder ? { orderNumber: createdOrder.orderNumber } : skipToken,
    {
        pollingInterval: (data) => (data?.paymentStatus === 'FAILED' ? 0 : 1000),
    }
);

With this in place, if a payment fails:

  • a second submission of the form would trigger a new createOrder mutation
  • when this resolves with a new order number, a new query would be triggered
  • when the data updates, the query would start polling again

I then no longer need local state or to unwrap my mutation result in handleSubmit.

Tanstack Query has a similar API via refetchInterval - https://tanstack.com/query/latest/docs/framework/react/reference/useQuery

The possible drawbacks I can see are:

  • May not be clear how to restart polling after data has reached a certain state. You essentially need to call refetch, and hope the data has changed, or change the query args
  • If including flags like isSuccess, isFetching etc., it may not be clear if pollingInterval is only evaluated when data changes, or when the flags themselves change.

I think this could be done without a breaking change given the option currently only accepts a number.

harry-gocity avatar Feb 14 '24 11:02 harry-gocity