Silvère MAZIERE
Silvère MAZIERE

Reputation: 125

Can't get componentDidUpdate() stop looping

I'm trying to fetch data from a public API with Axios, and display what I get through a React app. But I can't find a condition in my componentDidUpdate() to make it render only once, when the user modifies input. Anyone'd have an idea ?

Here's my code :

import React, { Component } from 'react';
import axios from "axios";
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      input: "",
      output: []
    }
  }

  componentDidUpdate() {
    axios.get(`https://geo.api.gouv.fr/communes?nom=${this.state.input}`)
      .then(response => {
        this.setState((prevState) => {
          if (prevState.input !== this.state.input) {
            return { output: response.data };
          }
        });
      })
      .catch(function (error) {
        console.log(error);
      })
  }

  handleInput = (event, input) => {
    this.setState({input: event.target.value});
  }

  render() {
    return (
      <React.Fragment>
        <label>Recherche : <input type="text" onChange={this.handleInput} /></label>
        <div>
          {this.state.output.map((value, index) => <p key={index}>{value.nom}</p>)}
        </div>
      </React.Fragment>
    );
  }
}

export default App;

Thanks for helping.

Upvotes: 7

Views: 7881

Answers (5)

Shubham Khatri
Shubham Khatri

Reputation: 281764

Assuming you need to take an action everytime your state change, you need to check if the state has updated before triggering an action in componentDidUpdate otherwise API call will be made whenever your component updates.

If you only wish to call the API once, then call it in componentDidMount

componentDidUpdate(prevProps, prevState) {

    if(prevState.input !== this.state.input) {
        axios.get(`https://geo.api.gouv.fr/communes?nom=${this.state.input}`)
          .then(response => {
            this.setState({ output: response.data });
          })
          .catch(function (error) {
            console.log(error);
          })
    }
}

Upvotes: 3

Silv&#232;re MAZIERE
Silv&#232;re MAZIERE

Reputation: 125

Assuming you need to take an action everytime your state change...

Actually, this is what I wanted to do, and the solution I've found is to wrap the Axios request in a

if(prevState.input !== this.state.input) { --- }

condition, just as Shubham told me to do. Nevertheless, I had to remove the setState() inner condition to make it work.

Thanks to everyone !

Upvotes: 0

ChrisR
ChrisR

Reputation: 4008

The only moment when you want to get data, is when your input has changed. Why not use the method to trigger the fetch? The state is used to keep track of what is entered and retrieved, but you should not rely on the state change to trigger function dependent on user changes.

See your example transposed to what I think is a good example, with my comments: https://codesandbox.io/s/oxoxoym85y

Upvotes: 0

bennyh
bennyh

Reputation: 29

If you need to call the API once You can use componentWillMount, once the state changed when the data arrives your component will rerender

Only if you want to use your API anytime the component rerender you can use componentDidUpdate, in this case make sure you are not changing the state to avoid infinite loop.

Upvotes: 0

Dario
Dario

Reputation: 6280

You are looping in your code: after your component is updated, componentDidUpdate will be invoked; inside this function you are calling setState which will re-render the component. This will trigger componentDidUpdate another time and so on... I think you should just move your API call from componentDidUpdate to componentDidMount.

Upvotes: 1

Related Questions