Search code examples
javascriptreactjsreact-functional-component

Can't perform React state update on an unmounted component on making go to top btn


im working on go to top button component and i dont understand what's going on. isBtnShown controls styles to button

export const TopBtn = () => {
    const [isBtnShown, setIsBtnShown] = useState(false)

    const trackScroll = () => {
        if (!isBtnShown && window.pageYOffset > 400) {
            setIsBtnShown(true)
        } else if (isBtnShown && window.pageYOffset <= 400) {
            setIsBtnShown(false)
        }
    }

    const backToTop = () => {
        if (window.pageYOffset > 0) {
            window.scrollTo({ top: 0, behavior: 'smooth' })
        }
    }
    window.addEventListener('scroll', trackScroll)
    useEffect(() => {
        return () => {
            window.removeEventListener('scroll', trackScroll)
        }
    }, [])
    return (
        <button className={isBtnShown ? `${s.topBtn} ${s.shown}` : `${s.topBtn}`} onClick={backToTop}>
            <ChevronUpIcon />
        </button>
    )
}

here's error

Warning: 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. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. at TopBtn (http://localhost:4000/src/features/TopBtn/TopBtn.jsx?t=1625128489631:21:39) at div


Solution

  • window.addEventListener('scroll', trackScroll)
        useEffect(() => {
            return () => {
                window.removeEventListener('scroll', trackScroll)
            }
        }, [])
    

    should probably be

        useEffect(() => {
            const trackScroll = () => {
                if (!isBtnShown && window.pageYOffset > 400) {
                    setIsBtnShown(true)
                } else if (isBtnShown && window.pageYOffset <= 400) {
                   setIsBtnShown(false)
                }
            }
            window.addEventListener('scroll', trackScroll)
            return () => {
                window.removeEventListener('scroll', trackScroll)
            }
        }, [isBtnShown])
    

    note, dependency on isBtnShown, and both the add and remove listener inside the useEffect