Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReactJS: how to call useEffect hook only once to fetch API data

Using React, I have the following functional component where I make use of useEffect():

import React, { useEffect } from 'react';
import { connect } from 'react-redux';

const MessagingComponent = React.memo(({ loadMessages, messages, ...props }) => {

  useEffect(() => {
    loadMessages();
  });

  return <div {...props}>These are the messages</div>;
});

const mapDispatchToProps = dispatch => ({
  loadMessages: () => dispatch({ type: 'LOAD_MESSAGES' })
});

const mapStateToProps = state => {
  return {
    messages: state.chatt.messages.chatMessages
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(MessagingComponent);

As you can see, I have an effect callback that calls the loadMessages() function in the useEffect() callback in my MessagingComponent:

  useEffect(() => {
    loadMessages();
  });

The call to loadMessages() loads messages which causes the component to re-render. This behaviour is as expected, however the problem is that the re-render causes the useEffect() hook to fire again, which causes loadMessages() to be called again. This in turn loads the messages from the back-end and causes the component to render again, and the cycle repeats.

How can I avoid this? Should I simply put an if condition in the useEffect() hook, and check for the messages property?

like image 508
tmp dev Avatar asked Apr 02 '19 21:04

tmp dev


People also ask

How do you call useEffect only once in React?

Side Effect Runs Only Once After Initial Render You do not want to make this API call again. You can pass an empty array as the second argument to the useEffect hook to tackle this use case. useEffect(() => { // Side Effect }, []); In this case, the side effect runs only once after the initial render of the component.

How do I call API only once in React?

Use the useEffect hook to only call a function once in React. When the useEffect hook is passed an empty dependencies array, it is only run when the component mounts. This is the preferred approach when you have to fetch data when the component mounts.

Why useEffect is calling again and again?

If your application is behaving strangely after updating to React 18, the default behavior of useEffect changed to run it 2 times. Just in development mode, but this is the mode everyone builds their application on.


2 Answers

If I understand correctly, you want to mimic the "on component mounted" behaviour of regular class based react components via useEffect(), so that the effect callback only fires once on the first render.

To achieve that behaviour, you can pass an empty array to the second argument of useEffect() like so:

useEffect(() => {
  loadMessages();
}, []); /* <-- add this */

The second array argument allows you to specify which prop variables trigger the useEffect() callback (if any) when their value(s) change.

By passing an empty array, this means that useEffect() won't be triggered by any changes to input prop variables and will in turn only ever be called once, during the first render.

For more information on this second argument, see this documentation and this documentation

like image 114
Dacre Denny Avatar answered Sep 23 '22 04:09

Dacre Denny


useEffect() is a react hook for functional components which covers the functionality of the lifecycle hooks of the class components. useEffect() combines componentDidMount(), componentDidUpdate() and componentWillUnmount() of the class components, which means it will execute when the component is mounted, when the state of the component changes and when the component is unmounted from the DOM.

Check below example,

useEffect(() => {
   setTimeout(() => {
      alert("data saved");
    }, 1000);
});

Above code/alert is executed when the component is mounted/initial render, when state of the component changes and when component is unmounted from the DOM.

To make the above code run less often we can pass an array of values as the second argument, as the dependencies for that effect. If one of the dependencies change the effect will run again.

Consider we have persons array passed into the props of the functional component and we want the alert to execute only when the persons array change.

useEffect(() => {
    setTimeout(() => {
      alert("data saved");
    }, 1000);
  }, [props.persons]);

This will execute the code during initial render and when props.persons changes.

So to execute the code only during initial render/component mount you can pass an empty array which means no dependencies specified when the effect should execute. Hence run during initial render and when the component is unmounted.

useEffect(() => {
    setTimeout(() => {
      alert("data saved");
    }, 1000);
  }, []);

You can modify your useEffect() hook as below to call it only once to fetch API data,

useEffect(() => {
    loadMessages();
  }, []);
like image 22
Hetal Rachh Avatar answered Sep 25 '22 04:09

Hetal Rachh