简体   繁体   中英

ReactJS - axios request at componentDidUpdate()

I've learned that, in Reactjs:

componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.

I have this method in my component that sends a network request with axio:

getFeatures(event) {
    const data = {
      spotify_token: this.props.spotifyToken
    };
    var headers = {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': true,
        Authorization: `Bearer ${window.localStorage.authToken}`
      }
    const url = `${process.env.REACT_APP_WEB_SERVICE_URL}/features/${this.props.userId}/${this.props.spotifyToken}`;
    axios.post(url, data, {headers: headers})
      .then((res) => {
        console.log(data);
      })
      .catch((err) => {
      });
  };

The getFeatures() method is called after props are obtained, like so:

componentDidUpdate(){
  this.getFeatures();
}

The call ends up working however, before it works it throws a 404 error and a malformed POST request, while both required props have not yet been updated to their values:

  1. POST http://localhost/features//undefined 404 (NOT FOUND)
  2. XHR finished loading: POST "http://localhost/features/1/undefined"

How do I fix these errors before it works?

It's because your spotifyToken and userId props are not ready when the component is updated and getFeatures is called. Check if they're defined.

Also, calling the API on every update of the UI is not a good idea. Do some validity check like this:

componentDidUpdate(prevProps) {
  const { spotifyToken, userId } = this.props;
  if (spotifyToken && userId && (spotifyToken !== prevProps.spotifyToken || userId !== prevProps.userId)) {
    this.getFeatures();
  }
}

You will want to add some conditionals to your componentDidUpdate to ensure that getFeatures isn't called every single time a prop or state changes. After initial mount, every single change will trigger this, which is probably why you are getting the weird errors with undefined in the URL.

componentDidUpdate(prevProps){
  const { spotifyToken, userId } = prevProps;

  if (spotifyToken && userId && (spotifyToken !== this.props.spotifyToken || userId !== this.props.userId)) {
    this.getFeatures();
  }
}

Since it eventually succeeds, that makes me think that either spotifyToken or userId are being updated after your component has initially mounted, but one or the other is undefined , an empty string, or something else incorrect.

FWIW, using the latest version of React with hooks helps tidy this up immensely. You can just add your dependencies after useEffect and that takes care of the messy logic of that conditional.

useEffect(() => {
  if (spotifyToken && userId) getFeatures();
}, [spotifyToken, userId]);

This is likely a component life-cycle/state issue, where prop 's that are required for your component to function correctly are not guaranteed to be present or defined throughout your components life span.

A simple solution would be to apply defensive programming principles to your getFeatures() method:

getFeatures(spotifyToken, userId) {

  /* If required props are not defined, early exit as the request
     cannot be completed */
  if (!spotifyToken || !userId) {
    return;
  }

  /* The required props are present so attempt network request */
  var headers = {
    'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': true,
    Authorization: `Bearer ${window.localStorage.authToken}`
  }

  const url = `${process.env.REACT_APP_WEB_SERVICE_URL}/features/${userId}/${spotifyToken}`;
  axios.post(url, data, {
      headers: headers
    })
    .then((res) => {
      console.log(data);
    })
    .catch((err) => {
      console.error(err);
    });
}

componentDidUpdate(prevProps) {
    const { spotify_token, userId } = this.props;

    if(prevProps.spotify_token !== spotify_token || prevProps.userId !== userId) {
        /* If a change occurred in token or user id, attempt to get 
        features */
        this.getFeatures(spotify_token, userId);
    }
}

These changes mean that once all required prop data is present, your network request will be performed.

If for instance, both the spotify_token and userId props are supplied at a later time (ie after a user interaction like a button click), then the componentDidUpdate() method will be called and the network request will be attempted.

Hope that helps!

As per the docs, you should compare current props to previous props before causing side effects like API calls or state updates.

Could you update the componentDidUpdate to something like this and try again -

componentDidUpdate(prevProps) {
  // Typical usage (don't forget to compare props):
  if (this.props.spotifyToken !== prevProps.spotifyToken) {
    this.getFeatures(this.props.spotifyToken)
  }
}

Additionally you should add a validation for this.props.spotifyToken for empty or undefined and not make the API call.

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