skip to Main Content

I have this Feed component. The idea is to preserve scroll on multiple feeds (tabs), e.g. I’m on localhost/feed-one, I scroll to 500px, switch to localhost/feed-two, and when I return to feed 1, the scroll should be back on 500px. The following code works as expected, yet ESLint shows an error, and I want to fix it.

import React, { useState, useEffect } from 'react';
import { useParams, useLocation } from 'react-router-dom';
import Loader from '../Loader/Loader';
import Post from '../Post/Post';
import Data, { Post as PostType } from '../../emulateAPI/Data';
import useScrollRestoration from '../../hooks/useScrollRestoration';
import './Feed.scss';
interface FeedProps {
    data: Data;
}
const Feed: React.FC<FeedProps> = (props) => {
    const { data } = props;
    const { categorySlug } = useParams<string>();
    const [isLoading, setIsLoading] = useState<boolean>();
    const [posts, setPosts] = useState<PostType[]>([]);
    const location = useLocation();
    const reScroll = useScrollRestoration();

    useEffect(() => {
        setIsLoading(true);
            const category = data.getCategoryBySlug(categorySlug);
            if (category) {
                setPosts(data.getPostsByCategory(category));
            }
        setIsLoading(false);
    }, [categorySlug, data]);

    useEffect(() => {
        reScroll();
    }, [posts]); //I GET THE ERROR HERE: React Hook useEffect has a missing dependency: 'reScroll'.

    return (
        <>
            {isLoading ? (
                <Loader />
            ) : (
                <div className='feed'>
                    {posts.map((post, index) => (
                        <Post key={index} post={post} />
                    ))}
                </div>
            )}
        </>
    );
}
export default Feed;

My custom hook useScrollRestoration:

import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
const useScrollRestoration = () => {
    const location = useLocation();

    useEffect(() => {
        const onScroll = () => {
            sessionStorage.setItem(location.pathname, window.scrollY.toString());
        };
        window.addEventListener('scroll', onScroll);
        return () => {
            window.removeEventListener('scroll', onScroll);
        };
    }, [location.pathname]);

    return (() => {
        const scrollY = sessionStorage.getItem(location.pathname);
        window.scrollTo(0, +(scrollY || 0));
    });
};
export default useScrollRestoration;

The error is in the Feed component: "React Hook useEffect has a missing dependency: 'reScroll'. Either include it or remove the dependency array."
But if I do add reScroll in the useEffect’s dependency array, the useEffect gets called on location.pathname changes when new posts haven’t been loaded yet and the scroll is often impossible (e.g. I can’t scroll back to 500px because the posts haven’t loaded yet, React scrolls to say 250px, posts finally load, but the value is already overwritten to 250px). Can I fix this? Any help is much appreciated!!!

2

Answers


  1. Chosen as BEST ANSWER

    Here's what I've come up with using refs. I only want to call reScroll() when user has already switched feeds and all the relevant posts have been loaded, not just when location.pathname changes. So there's a need to indicate that the posts have been loaded, and setting state won't help because it won't take immediate effect. Working solution for my use case:

        const isRedirecting = useRef<boolean>();
    
    // On url change set a ref to indicate that user is switching feeds, but relevant posts haven't been loaded yet.
        useEffect(() => {
            isRedirecting.current = true;
        }, [location.pathname]);
    
        useEffect(() => {
            setIsLoading(true);
            setTimeout(() => {
                const category = data.getCategoryBySlug(categorySlug);
                if (category) {
                    setPosts(data.getPostsByCategory(category));
                }
                setIsLoading(false);
                isRedirecting.current = false; // Posts are loaded, feed switching done. Now just have to wait for setIsLoading(false) to take effect!
            }, 1000);
        }, [categorySlug, data, favs]);
    
    // Check if necessary state changes have been rendered.
        useEffect(() => {
            if (!isRedirecting.current) {
                reScroll();
            }
        }, [isLoading, reScroll]);
    

    I'm a noob, so let me know what you think.


  2. You can only call the rescroll when you have posts loaded like:

    useEffect(() => {
      if(!posts.length) return;
      reScroll();
    }, [posts, reScroll]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search