Raven
Raven

Reputation: 689

useEffect is running on initial load & on update - How do I limit to just changes?

In my code example, my useEffect is subscribed to postId, which is not changing at any point. Yet useEffect is still being run.

Is there a way to prevent this from running and only launch if I change it?

import React, {useState, useEffect} from 'react';
import Vim from './Vim';
import './Main.css';


function Main():JSX.Element { 

  const [postId,updatePostId] = useState<number|null>(null)
  const [content, updateContent] = useState<string>('default text');
  const apiUrl = 'http://127.0.0.1:8000/'

  useEffect(()=>{
    // Detect change in PostID & go download it.
    // Ignore if we are coming from null->number
    console.log('Why am I running?')
    fetch(apiUrl+'get_post/'+postId)
    .then(res=>res.json())
    .then(result=>console.log(result))
  },[postId])


  function loadPost(pid:number):string|null{
    // fetch from API, load post content
    console.log('I can access:'+postId)
    return null;
  }
  
  function backLinks():JSX.Element{
    
    return(
      <div className="backlinks">
      </div>
    )
  }

  return (
    <div className='main'>
      <Vim content={content} />
    </div>
  )
}

export default Main

Upvotes: 2

Views: 3174

Answers (1)

sadrzadehsina
sadrzadehsina

Reputation: 1349

In fact in the first run, postId is null since you initialize it in that way. So you need to take care of that as well.

useEffect either accepts an array of dependencies or you could leave it empty. In the later case it just run once since there is no dependency, however in the first case it both listen for dependencies changing and also runs when the component mounts for the first time.

useEffect(()=>{
  if (postId == null) return;
  ...
}, [postId]);

For a more general approach you can have something like below.

const [ready, setReady] = useState(false);

useEffect(() => {

  setReady(true);

}, [])

useEffect(()=>{
  if (!ready) return;
  ...
}, [postId, ready]);

The above solution is fair enough in most situations but I suggest you to handle it by creating a ref, assigning it to your DOM element and wait until that ref become available. So you are sure that your component is being rendered for the first time.

const isMounted = useRef(null);

useEffect(()=>{
  if (!isMounted.current) return;

  // do your fetch stuff here
  fetch(apiUrl+'get_post/'+postId)
    .then(res=>res.json())
    .then(result=>console.log(result))

}, [isMounted]);

<div ref={isMounted}></div>

This way you don't need an extra re-render since updating refs does not lead to a re-render.

Upvotes: 1

Related Questions