Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

useEffect re-renders too many times

Tags:

reactjs

I have this component, that needs to fetch data, set it to state and then pass it to the children. Some of the data also needs to be set in context. My problem is that using useEffect, once called the API, it will re-render for each setvalue() function I need to execute. I have tried passing to useEffect an empty [] array, still getting the same number of re-renders, due to the fact that the state is changing. At the moment the array is containg the set...functions to prevent eslint to throw warnings.

Is there a better way to avoid this many re-renders ?

const Home = (props) => {

  console.log("TCL: Home -> props", props);
  const classes = useStyles();
  const [value, setValue] = React.useState(0);


  //CONTEXT
  const { listSavedJobs, setListSavedJobs, setIsFullView} = useContext(HomeContext);
  const {
    setUserName,
    setUserLastName,
    setUserEmail,
    setAvatarProfile,
  } = useContext(UserContext);

  // STATE
  const [searchSettings, setSearchSettings] = useState([]);
  const [oppData, setOppData] = useState([]);
  const handleChange = (event, newValue) => {
    setValue(newValue);
  };


  const handleChangeIndex = index => {
    setValue(index);
  };


  //API CALLS
  useEffect(() => {
    const triggerAPI = async () => {

      setIsFullView(false);

      const oppResponse = await API.getOpportunity();
      if(oppResponse){
        setOppData(oppResponse.response);
      }
      const profileResponse = await API.getUserProfile();
      if(profileResponse){
        setUserName(profileResponse.response.first_name);
        setUserLastName(profileResponse.response.last_name);
        setUserEmail(profileResponse.response.emailId);
      }
      const profileExtData = await API.getUserProfileExt();
      if(profileExtData){
        setAvatarProfile(profileExtData.response.avatar);
        setListSavedJobs(profileExtData.response.savedJobs);
        setSearchSettings(profileExtData.response.preferredIndustry);
      }
    };
    triggerAPI();

  }, [ 
    setOppData,
    setUserName,
    setUserLastName,
    setUserEmail,
    setAvatarProfile,
    setListSavedJobs,
    setIsFullView,
  ]);

...```




like image 477
Andrea Avatar asked Jan 06 '20 02:01

Andrea


People also ask

Why is useEffect running so many times?

Changing state will always cause a re-render. By default, useEffect always runs after render has run. This means if you don't include a dependency array when using useEffect to fetch data, and use useState to display it, you will always trigger another render after useEffect runs.

How do you stop Rerendering in useEffect?

You can add a condition in the call back function that checks if a certain condition is met, e.g. if data is empty. If it is empty, then fetch data, otherwise do nothing. This will prevent the infinite loop from happening. const getData = useEffect(()=>{ const fetchData = () => { UserService.


2 Answers

Pass just an empty array to second parameter of useEffect.

Note

React guarantees that setState function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list. Source

Edit: Try this to avoid rerenders. Use with caution

like image 83
Alexander Vidaurre Arroyo Avatar answered Oct 20 '22 22:10

Alexander Vidaurre Arroyo


Only Run on Mount and Unmount

You can pass the special value of empty array [] as a way of saying “only run on mount and unmount”. So if we changed our component above to call useEffect like this:

useEffect(() => {
  console.log('mounted');
  return () => console.log('unmounting...');
}, [])

Then it will print “mounted” after the initial render, remain silent throughout its life, and print “unmounting…” on its way out.

Prevent useEffect From Running Every Render

If you want your effects to run less often, you can provide a second argument – an array of values. Think of them as the dependencies for that effect. If one of the dependencies has changed since the last time, the effect will run again. (It will also still run after the initial render)

const [value, setValue] = useState('initial');

useEffect(() => {
  // This effect uses the `value` variable,
  // so it "depends on" `value`.
  console.log(value);
}, [value])

For more clarification useEffect

like image 22
akhtarvahid Avatar answered Oct 20 '22 21:10

akhtarvahid