gluestack-ui icon indicating copy to clipboard operation
gluestack-ui copied to clipboard

Using toast as useEffect dependency causes infinite calls.

Open endrits079 opened this issue 1 year ago • 2 comments

Description

toast causes useEffect to run infinitely if used as dependency, it apparently shouldn't do that

CodeSandbox/Snack link

No response

Steps to reproduce

Inside a component

  1. const toast = useToast()

  2. useEffect(()=>{ toast.show(...) },[toast,error])

  3. That's it

gluestack-ui Version

1.1.8

Platform

  • [ ] Expo
  • [X] React Native CLI
  • [ ] Next
  • [ ] Web
  • [X] Android
  • [X] iOS

Other Platform

No response

Additional Information

No response

endrits079 avatar Mar 13 '24 12:03 endrits079

@endrits079 Thanks for reporting this. We'll have a look.

surajahmed avatar Mar 15 '24 09:03 surajahmed

Hey @endrits079, this is an expected behaviour as useToast hook internally is accessing some context values. Calling toast.show() inside the useEffect hook is somehow causing the toast context values to update, which then creates a new toast object.

Since you have added toast in the dependency array, useEffect sees a new toast object, re-runs, and calls toast.show again. This cycle repeats, causing the infinite loop.

This is how our useToast hook internally works:

    const useToast = () => { 
    const { AnimationWrapper,
    AnimatePresence,
      setToast,
      hideAll,
      isActive,
      hideToast,
    } = React.useContext(ToastContext);
    AnimatePresence.current = StyledAnimatePresence;
    AnimationWrapper.current = StyledAnimationWrapper;
    const toast = useMemo(
      () => ({
        show: setToast,
        close: hideToast,
        closeAll: hideAll,
        isActive,
      }),
      [setToast, hideAll, isActive, hideToast]
    );
    return toast; 
    };

A possible solution for this is to empty the dependency array. If you only want the toast to show once on initial render, change the useEffect dependency array to []. This prevents re-runs on subsequent changes to toast.

DaminiPandey avatar May 09 '24 06:05 DaminiPandey

Hey @endrits079 , we are closing this issue for now, please feel free to reopen the issue if you are still facing it/ having any queries about it.

sra1kumar-NULL avatar Sep 24 '24 08:09 sra1kumar-NULL

Hey @endrits079, this is an expected behaviour as useToast hook internally is accessing some context values. Calling toast.show() inside the useEffect hook is somehow causing the toast context values to update, which then creates a new toast object.

Since you have added toast in the dependency array, useEffect sees a new toast object, re-runs, and calls toast.show again. This cycle repeats, causing the infinite loop.

This is how our useToast hook internally works:

    const useToast = () => { 
    const { AnimationWrapper,
    AnimatePresence,
      setToast,
      hideAll,
      isActive,
      hideToast,
    } = React.useContext(ToastContext);
    AnimatePresence.current = StyledAnimatePresence;
    AnimationWrapper.current = StyledAnimationWrapper;
    const toast = useMemo(
      () => ({
        show: setToast,
        close: hideToast,
        closeAll: hideAll,
        isActive,
      }),
      [setToast, hideAll, isActive, hideToast]
    );
    return toast; 
    };

A possible solution for this is to empty the dependency array. If you only want the toast to show once on initial render, change the useEffect dependency array to []. This prevents re-runs on subsequent changes to toast.

Hey so this design is insane lol -- why are you re-creating the toast object every time isActive changes? Quite literally the only toast library I've ever seen do this, and definitely not expected behavior for any developer building on Gluestack (you'll just get infinite loops if you try to show toasts in a useEffect, like the OP)

giaset avatar Mar 05 '25 18:03 giaset