JetLagFox
JetLagFox

Reputation: 250

Handle infinite loop in componentDidUpdate

I am using a simple form with a text input. When something is written the state of the component is updated, and as this.setState is asynchronous, I fetch the data from the API on componentDidUpdate.

The problem is that I don't find the solution to avoid the infinite loop, as I update the state after using axios:

constructor(props) {
    super(props);
    this.state = {
      searchValue: "",
      data: null,
    };
    this.handleInputChange = this.handleInputChange.bind(this);
  }

handleInputChange(event) {
    this.setState({ searchValue: event.target.value });
}

componentDidUpdate() {
    const url = 'https://www.google.com/?search=' + this.state.searchValue;
    axios.get(url).then(response => this.setState({data: response.data}));
}

Upvotes: 2

Views: 3048

Answers (1)

Vasileios Pallas
Vasileios Pallas

Reputation: 4877

Since you want to fetch new data on input change, you can make the call directly in handleInputChange callback.

handleInputChange({target: {value}}) {
  this.setState({ searchValue: value });
  this.fetchData(value);
}

fetchData(text) {
  const url = 'https://www.google.com/?search=' + text;
  axios.get(url).then(({data}) => this.setState({data}));
}

Now, if you want to use componentDidUpdate you can compare previousState with your state.

handleInputChange({target: {value}}) {
  this.setState({ searchValue: value });
}

fetchData(text) {
  const url = 'https://www.google.com/?search=' + text;
  axios.get(url).then(({data}) => this.setState({data}));
}

componentDidUpdate(prevProps, prevState) {
  // only update if searchValue has changed
  if (prevState.searchValue !== this.state.searchValue) {
     this.fetchData(this.state.searchValue);
  }
}

Also with hooks:

const [searchValue, setSearchValue] = useState('');
const [data, setData] = useState(null);

function handleInputChange({target: {value}}) {
   setSearchValue(value);
}

function fetchData(text) {
   const url = 'https://www.google.com/?search=' + text;
   axios.get(url).then(({data}) => setData({data}));
}

useEffect(() => {
  // don't run on componentDidMount or if string is empty
  if (searchValue) {
    fetchData(searchValue);
  }
}, [searchValue]);

Update 2021

The above implementations work fine, but there is a slightly better way to handle the request inside useEffect:

    const [searchValue, setSearchValue] = useState('');
    const [data, setData] = useState(null);
    
    function handleInputChange({target: {value}}) {
       setSearchValue(value);
    }
    
    useEffect(() => {
      async function fetchData(text) {
         try {
            // don't run on componentDidMount or if string is empty
            if (text) {
              return
            }

            const url = `https://www.google.com/?search=${text}`;
            const { data } = await axios.get(url);
            setData({ data });
         } catch(e) {
           // handle error
         }
      };

      fetchData(searchValue);
    }, [searchValue]);

Upvotes: 9

Related Questions