简体   繁体   中英

Why am I only able to call a method after checking that it exists first?

In my render method, I can only render a random quote from fetched list of JSON quotes successfully if I check that the getter method used to get the quote (based on a random index) exists first. This works: {this.randomQuote ? this.randomQuote.quote : ''} {this.randomQuote ? this.randomQuote.quote : ''} , but if I just do {this.randomQuote.quote} , I get "TypeError: Cannot read property 'quote' of undefined." (Note that quote is the name of a property each object in an array of fetched JSON objects. So the randomQuote method selects an object from the JSON based on the random index that the randomQuoteIndex() method picks, but the quote property is selected only in the rendering.) How could it be that just checking to see if this.randomQuote isn't undefined first would enable me to call render this.randomQuote.quote ?

This is the working version of the class:

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      quotes: [],
      randomQuoteIndex: null
    }
  }

  componentDidMount() {
    // fetch method uses GET method by default; don't need to specify
    fetch('https://gist.githubusercontent.com/nataliecardot/0ca0878d2f0c4210e2ed87a5f6947ec7/raw/1802a693d02ea086817e46a42413c0df4c077e3b/quotes.json')
      // Takes a JSON response string and parses it into JS object
      .then(response => response.json())
      // state is set to quotes: quotes due to destructuring
      .then(quotes => this.setState({ quotes }, () => {
        this.setState({ randomQuoteIndex: this.randomQuoteIndex() })
      }));
  }

  get randomQuote() {
    return this.state.quotes[this.state.randomQuoteIndex];
  }

  randomQuoteIndex() {
    return random(0, this.state.quotes.length - 1);
  }

  render() {
    return (
      <div className="App" id="quote-box">
        {/* Since this is a get function, can call it as though it were a regular variable. Also, in this.random.quote, quote is the name of a property in each of the fetched JSON quote objects (that are held in an array) */}
        {this.randomQuote ? this.randomQuote.quote : ''}
        <Button
          buttonDisplayName="Next"
          clickHandler={this.buttonClickHandler}
        />
      </div>
    );
  }
}

Docs :

The componentDidMount() method runs after the component output has been rendered to the DOM.

...so the first time it's rendered, this.state.randomQuoteIndex is null , this.state.quotes[null] is undefined , undefined.quote is bad.

If you put the check in, you survive the first render, and live to see the component when it's properly initialised.

Any time you have a component that may be caught naked and bashful, you want to make sure it can still render something correctly (and either hide, or display a spinner or something similar, till it's done dressing). Ideally, it would be by having an explicit flag in its state saying "I'm decent now", not by checking a function that may return undefined due to a near-miss with a bug. :)

Your randomQuoteIndex function probably selects an index out of range, you should change it to:

randomQuoteIndex() {
  return Math.floor(Math.random() * this.state.quotes.length);
}

No need to use length -1, see https://www.w3schools.com/js/js_random.asp

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