Isaac
Isaac

Reputation: 361

useEffect re-renders too many times

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,
  ]);

...```




Upvotes: 9

Views: 26159

Answers (3)

iqoOopi
iqoOopi

Reputation: 179

If you are using React 18, this won't be a problem anymore as the new auto batching feature: https://reactjs.org/blog/2022/03/29/react-v18.html#new-feature-automatic-batching

If you are using an old version, can refer to this solution: https://statics.teams.cdn.office.net/evergreen-assets/safelinks/1/atp-safelinks.html

Upvotes: 0

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

Upvotes: 8

akhtarvahid
akhtarvahid

Reputation: 9769

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

Upvotes: 4

Related Questions