1

I'm pretty new to React Hooks and I need some guidance with the following issue.

I'm submitting a form asynchronously. I do use a Promise that can be canceled for that.

Now, imagine that something happens until the promise resolves and the Component gets unmounted. When the promise resolves I get an error:

Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application.

Basically I must cancel the Promise if the component gets unmounted. The return function of the useEffect would be exactly what I need, but the problem is that I cannot start the Promise request inside useEffect function since the request must be performed when the submit button is pressed and not when the component is mounted or updated.

How can the Promise be canceled when the component unmounts?

Note: I'm looking for a solution that doesn't involve isMounted that has been declared an anti-pattern according to [react doc] [react doc]: https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html


Minimal Code Example:

const AccountDetails = ({ props }) => {

/* functions and variable declarations go here */
const [accountDetails, setAccountDetails] = useState({}); //initialize state variables 

const handleSubmit = () => {
  const data = { /* computed data */ }; 
  dataService.get(
    submitUrl,
    data, 
  ).then(
    result => {
     setAccountDetails(result); //here is where the error is thrown when the component is unmounted
    }
  );  
};

return (
  <div>
   <!-- form fields --> 
   <button onClick={handleSubmit} .../>

  </div>
);

};

I've tried the following solution, that oddly enough doesn't work .

 const AccountDetails = ({ props }) => {

    /* functions and variable declarations go here */
    const [accountDetails, setAccountDetails] = useState({}); //initialize state variables 
    let submitAccountDetailsPromise = null;

    useEffect(() => {
        return () => {
         /*DON'T UNDERSTAND HOW COME submitAccountDetailsPromise == NULL WHEN this code is executed after the submit button was pressed. */
         submitAccountDetailsPromise && submitAccountDetailsPromise.cancel();
        };
    }, []);

    const handleSubmit = () => {
      const data = { /* computed data */ }; 
      submitAccountDetailsPromise  = dataService.get(
        submitUrl,
        data, 
      ).then(
        result => {
         setAccountDetails(result); //here is where the error is thrown when the component is unmounted
        }
      );  
    };

    return (
      <div>
       <!-- form fields --> 
       <button onClick={handleSubmit} .../>

      </div>
    );

    };
RaresI
  • 461
  • 1
  • 6
  • 19
  • 1
    Show some code [How to create a Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) – Dennis Vash Aug 07 '19 at 07:06
  • this might be helpful https://stackoverflow.com/questions/44457788/cancel-a-promise-when-a-component-is-unmounted-in-reactjs – Akhil Aravind Aug 07 '19 at 07:08
  • @AkhilAravind, thanks for helping out. I've read about that solution, but it seems that using `isMounted` is now an anti-pattern. https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html – RaresI Aug 07 '19 at 07:11
  • @RaresI, in functional component, `useEffect(()=>{ return () => // promise cancellation call })` does this work. ? – Akhil Aravind Aug 07 '19 at 07:15
  • @RaresI refered this link https://reactjs.org/docs/hooks-effect.html – Akhil Aravind Aug 07 '19 at 07:15
  • @RaresI: As simple solution you can maintain some local var which will keep status of your component's status. If component gets unmount then you doesnt need to setState() on same component. This can solve your issue. – Tejas Aug 07 '19 at 07:18
  • @AkhilAravind I've tried that. I mean using `useEffect` just for the return function to know when the component unmounts. I declare a variable `let submitPromise = null;` and on the submit handling function I set the value `submitPromise = dataService.get(/*the code here*/)`. Then I have `useEffect(() => { return () => { submitPromise && submitPromise.cancel(); }; });` Strangely enough the `submitPromise == null` when the `useEffect` return function is invoked. – RaresI Aug 07 '19 at 07:19
  • @Tejas wouldn't that be similar to the 'isMounted' solution? Which is not accepted anymore https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html – RaresI Aug 07 '19 at 07:24
  • @RaresI on `return() => ` call the cancel in the parent component, like `return()=> props.cancelPromise()`. this also works right ? – Akhil Aravind Aug 07 '19 at 07:27
  • @RaresI: Thats why I am referring my comment as simple way its kind of similar but probably reliable. And even its looking same but doesn't have any relation with isMounted(). – Tejas Aug 07 '19 at 07:28
  • @AkhilAravind, please check the sample code I've added recently – RaresI Aug 07 '19 at 07:40
  • 1
    @Tejas, I'll give it a try and see how it goes. – RaresI Aug 07 '19 at 07:41
  • @RaresI, can you try changing `useEffect(() => { return () => { submitAccountDetailsPromise && submitAccountDetailsPromise.cancel(); }; }, []);` to `useEffect(() => { return () => { submitAccountDetailsPromise && submitAccountDetailsPromise.cancel(); }; }, submitAccountDetailsPromise); – Akhil Aravind Aug 07 '19 at 08:21
  • @AkhilAravind it doesn't work. I've also tried adding the submitAccountDetailsPromise to the state. – RaresI Aug 07 '19 at 08:31
  • @RaresI this could solve your issue, the same describes the issue you just got " https://juliangaramendy.dev/use-promise-subscription/ Please update here if it helped. – Akhil Aravind Aug 07 '19 at 08:35
  • @AkhilAravind, that solution seems legitimate, but the problem is I cannot create the promise inside `useEffect`, since I need to start the request when the submit button is clicked not when the component is mounted. – RaresI Aug 07 '19 at 08:56
  • move the functions to submit function will do the job, right – Akhil Aravind Aug 07 '19 at 08:57
  • @AkhilAravind sure, but I won't know when the component is unmounted without an useEffect in this functional Component – RaresI Aug 07 '19 at 09:25

0 Answers0