Search code examples
javascriptreactjspromisereact-hookses6-promise

React cancel out of Promise on props change


I have a React component that takes a visible bool prop. The react executes a function which triggers a long running Promise, and I need to cancel out of the Promise when visible changes. Trying to externally reject the Promise does not stop the execution however.

A rough outline of the code would look like:

<ComponentA visible={visible} />
const ComponentA = ({ visible }) => {
   const [rejectFn, setRejectFn] = useState(() = () => console.log('test');

    useEffect(() => {
        if (visible) {
            ...
        } else {
            setRejectFn();
        }
    }, [visible]);

   const promiseFn = () => (
       new Promise((resolve, reject) => {
            // logic

            const endCallback = (error) => {
                if (error) {
                    reject(error);
                    console.log('error', error);
                } else {
                    resolve(...);
                }
            };

            setRejectFn(() = () => endCallback('exit early'));

            someOtherFn(..., endCallback); // processing file chunk by chunk
       })
   );

   const onDivClick = () => (
      // set some state vars
      promiseFn()
          .then(() => ...)
          .catch(() => ...)
          .finally(() => ...);
   );

   return (
      <div onClick=(onDivClick) />
   );
}

The above code will fire off the rejectFn and log "exit early", however the Promise will continue to execute until completion.


Solution

  • Here you go :

    You can't cancel the ongoing promise request, but you can do something like this

      const visibleRef = useRef(visible); // using ref to get latest value inside setInterval
    
      useEffect(() => {
        visibleRef.current = visible;
        console.log("visible", visible);
      }, [visible]); // <--- Set the value of visible whenever it changes
    
      const promiseFn = () =>
        new Promise((resolve, reject) => {
          // logic
    
          // Will check visibleRef.current every second , you can reduce the time as your need
          const interval = setInterval(() => {
            if (!visibleRef.current) { 
              reject("Due to Visible false value");
              clearInterval(interval);
            }
          }, 1000); 
    
          // just to mock the promise event execution
          setTimeout(() => {
            resolve();
            clearInterval(interval);
          }, 5000);
        });
    
    

    WORKING DEMO

    Please check the console, that will help you to get demo's understanding

    Edit #SO-Promise-cancel-issue