Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

153
Visualizações
Why is functional component nested function using a stale state value?

My component fetches and displays posts using infinite scrolling (via an IntersectionObserver). The API call that the component makes is dependent on the current number of fetched posts. This is passed as an offset to the API call.

The desired effect is that, if the posts array is empty, its length is 0 and my API will return the first 5 posts. When the last post in the UI intersects with the viewport, another fetch is made, but this time the API call is passed an offset of 5, so the API skips the first 5 posts in the collection and returns the next 5 instead.

Here is my code:

export default function Feed() {
  const [posts, setPosts] = useState([]);

  const fetchPosts = async () => {
    const newPosts = await postAPI.getFeed(posts.length);
    setPosts((prevPosts) => [...prevPosts, ...newPosts]);
  };

  useEffect(fetchPosts, []);

  const observerRef = useRef(
    new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) {
        observerRef.current.unobserve(entry.target);
        fetchPosts();
      }
    })
  );

  const observePost = useCallback((node) => node && observerRef.current.observe(node), []);

  return (
    <>
      {posts.map((post) => {
        const key = post._id;
        const isLastPost = key === posts.at(-1)._id;
        const callbackRef = !isLastPost ? undefined : observePost; //only if last post in state, watch it!
        
        return <PostCard {...{ key, post, callbackRef }} />;
      })}
    </>
  );
}

However, every time the fetchPosts function is called, the value it uses for posts.length is 0 - the number the function was originally created with.

Can someone explain to me why the closure over posts.length is stale here? I thought that every time a component re-renders, all nested functions within it were recreated from scratch? As such, surely the fetchPosts function should be using the latest value of posts.length every time it is called? Any help is appreciated! :)

about 4 years ago · Juan Pablo Isaza
1 Respostas
Responde à pergunta

0

You are creating a new IntersectionObserver on every render - not a good idea. However, only the first of all these created observers, the one you store in the ref (the ref that is never updated), is the one on which you call observe(), and that first observer uses a stale closure over the initial value of posts.

Instead, I would suggest creating the intersection observer(s) inside the observePost function:

const [posts, setPosts] = useState([]);

const fetchPosts = useCallback(async () => {
  const newPosts = await postAPI.getFeed(posts.length);
  setPosts((prevPosts) => [...prevPosts, ...newPosts]);
}, [posts.length]);
//  ^^^^^^^^^^^^

useEffect(fetchPosts, []);

const observePost = useCallback((node) => {
  if (!node) return;
  const observer = new IntersectionObserver(([entry]) => {
    if (entry.isIntersecting) {
      observer.unobserve(entry.target);
      fetchPosts();
    }
  });
  observer.observe(node);
}, [fetchPosts]);
//  ^^^^^^^^^^

Important here is the dependency of the observePost callback on the fetchPosts callback which has a dependency on the length of posts, to avoid getting stale. It's probably also possible to solve this with refs, but I don't think they're necessary here.

about 4 years ago · Juan Pablo Isaza Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda