简体   繁体   中英

Guess the number with React - what is the proper way of using state in that case

My React client app should guess the secret number between 1 and 10 000. Here is my code:

import axios from 'axios';
import React, { Component } from 'react';

class GuessEngine extends Component {
  constructor(props) {
    super(props);

    this.state = {
      number: null,
      result: null,
    };
  }

  componentDidMount() {
    const firstGuess = 5000;
    axios
      .post('http://localhost:3001/number', {
        isNumber: firstGuess,
      })
      .then(response => {
        const { resultCode } = response.data;
        this.setState({ number: firstGuess });
        this.setState({ result: resultCode });
      })
      .catch(error => console.log(error));
  }

  componentDidUpdate(prevProps, prevState) {

    if (prevState !== this.state) {
      if (this.state.result === 'lower') {
        const newNumber = this.state.number - 1;
        axios.post('http://localhost:3001/number', {
          isNumber: newNumber,
        });
        this.setState({ number: newNumber });
      }

      if (this.state.result === 'higher') {
        const newNumber = this.state.number + 1;
        axios.post('http://localhost:3001/number', {
          isNumber: newNumber,
        });
        this.setState({ number: newNumber });
      }

      if (this.state.result === 'success') {
        console.log(`Success! The secret number is ${this.state.number}!`);
      }
    }
  }

  render() {
    return <div>Test</div>;
  }
}

export default GuessEngine;

And I'm getting an error like this:

Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

So if I can't use componentDidUpdate like this, what is the proper way to use React Lifecycle Hooks to make it work?

My app has sent 1011 request and than it crashed.


SOLUTION

So using @John_Ruddell answer I came up with this solution:

 componentDidUpdate() {
    if (this.state.result !== 'success') {
      if (this.state.result === 'lower') {
        const newNumber = this.state.number - 1;
        axios
          .post('http://localhost:3001/number', {
            isNumber: newNumber,
          })
          .then(response => {
            const { resultCode } = response.data;
            this.setState({ result: resultCode, number: newNumber });
          });
      } else if (this.state.result === 'higher') {
        const newNumber = this.state.number + 1;
        axios
          .post('http://localhost:3001/number', {
            isNumber: newNumber,
          })
          .then(response => {
            const { resultCode } = response.data;
            this.setState({ result: resultCode, number: newNumber });
          });
      }
    } else if (this.state.result === 'success') {
      console.log(`Success! The secret number is ${this.state.number}!`);
    } else {
      console.log(`Sorry! Some errors occured!`);
    }
  }

This code does not compares this.state.number !== prevState.number , but only in this way I forced it to work

you are setting state every time the component did update is firing.. instead of waiting for a callback from the request to see if its lower or higher

also you should put the logic of state transitions into better conditionals

const nextNum = { lower: -1, higher: 1 } // simple mapping of the next guess so you can reduce amount of code
componentDidUpdate(prevProps, prevState) {
    if (this.state.result && this.state.number !== prevState.number) {
        if (nextNum[this.state.result]) {
            // make request with the number
             const newNumber = nextNum[this.state.result]
             axios.post('http://localhost:3001/number', {
                 isNumber: newNumber,
             }).then(response => {
                 const { resultCode } = response.data;
                 this.setState({result: resultCode, number: newNumber})
             }).catch(error => console.log(error));
        } else if (this.state.result === 'success') {
            console.log(`Success! The secret number is ${this.state.number}!`);
        }
    }
}

Note the key thing here is you should only setState AFTER the request comes back.. or else you will just endlessly setState.. because this.state.result will never be updated to be success

setState will lead to the update so your componentDidUpdate will be called over and over again:

componentDidUpdate(prevProps, prevState) { 
  // after each update this will be true and further runing another updates
  if (prevState !== this.state) {  
  }
}

You need better logic to decipher whether you need to do an update or not. Meaning at some point componentDidUpdate should not do anything.

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