简体   繁体   中英

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?

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM