简体   繁体   中英

How to properly fetch data from an api in a class based react component?

I am trying to fetch data from an external source(springboot) and populate it to my class based react component in place of the dummy data. But I am getting an error which I am not clearly able to identify as to why this is thrown.

My api from springboot contains an array of objects which looks like below:

[{"id":1,"question":"What is the capital of France","answer":"PARIS"},{"id":2,"question":"What is the capital of India","answer":"DELHI"},{"id":3,"question":"Excellent! Congratulations!","answer":"You Survived"}]

My React class looks like:

import React, { Component, useState } from 'react';
import Question from '../Question/Question';
import Keyboard from '../Keyboard/Keyboard';
import Puppet from '../Puppet/Puppet';
import GameStatus from '../GameStatus/GameStatus';
// import { questionsAndAnswers } from '../../gamedata';
import Confetti from 'react-confetti';
import './Game.css';

export class Game extends Component {
  constructor(props) {
    super(props);
    this.state = {
      questionsAndAnswers:[],
      triggerConfetti: false,
      gameCompleted: false,
      gameAlive: true,
      pressedLettersArray: [],
      currOp: 0,
      maxOps: 6,
      questionNumber: 0,
      keys: [
        'A',
        'B',
        'C',
        'D',
        'E',
        'F',
        'G',
        'H',
        'I',
        'J',
        'K',
        'L',
        'M',
        'N',
        'Ñ',
        'O',
        'P',
        'Q',
        'R',
        'S',
        'T',
        'U',
        'V',
        'W',
        'X',
        'Y',
        'Z'
      ]
    };
  }
  async componentDidMount() {
    fetch("http://localhost:8080/qna/all")
        .then(result=>result.json())
        .then((result)=>{
            this.setState({questionsAndAnswers: result})
            console.log("Recieved Details")
        })
    
    // const response = await fetch("http://localhost:8080/qna/all");
    // const body = await response.json();
    // console.log(body);
    // this.setState({questionsAndAnswers: body});
    if (this.state.gameCompleted === true) {
      this.setState({
        triggerConfetti: true
      });
    }
  }


  handlePressedLetter = letter => {
    const pressedLettersArray = [...this.state.pressedLettersArray] || [];
    const word = this.state.questionsAndAnswers[this.state.questionNumber].answer;
    if (word.includes(letter)) {
      this.setState({
        pressedLettersArray: [...pressedLettersArray, letter]
      });
    } else {
      this.setState({
        currOp: this.state.currOp + 1
      });
      if (this.state.currOp >= this.state.maxOps) {
        this.setState({
          gameAlive: false
        });
      }
    }
  };

  resetGame = () => {
    this.setState({
      triggerConfetti: false,
      gameCompleted: false,
      gameAlive: true,
      currOp: 0,
      questionNumber: 0,
      pressedLettersArray: []
    });
  };

  
  
  componentDidUpdate = () => {
    const wordLettersArr = this.state.questionsAndAnswers[this.state.questionNumber].answer.split('');
    const keysPressedArr = this.state.pressedLettersArray;
    const complete = wordLettersArr.every(letter => keysPressedArr.includes(letter));
    if (complete) {
      if (this.state.questionNumber < this.state.questionsAndAnswers.length - 2) {
        this.setState({
          pressedLettersArray: [],
          currOp: 0,
          questionNumber: this.state.questionNumber + 1
        });
      } else if (
        this.state.questionNumber === this.state.questionsAndAnswers.length - 2 &&
        this.state.gameCompleted === false
      ) {
        this.setState({
          gameCompleted: true,
          currOp: 0,
          pressedLettersArray: this.state.questionsAndAnswers[this.state.questionNumber + 1].answer.split(''),
          questionNumber: this.state.questionNumber + 1
        });
      }
    }
    if (this.state.gameCompleted === true && this.state.triggerConfetti === false) {
      this.setState({
        triggerConfetti: true
      });
    }
  };
  render() {
   
    const word = (this.state.questionsAndAnswers[this.state.questionNumber] && this.state.questionsAndAnswers[this.state.questionNumber].answer);
    return (
      <div className="game">
        {this.state.triggerConfetti ? (
          <Confetti
            height={document.querySelector('.game').offsetHeight}
            width={document.querySelector('.game').offsetWidth}
          />
        ) : null}
        <h2>Welcome</h2>

        <Puppet
          questionNumber={this.state.questionNumber}
          currOp={this.state.currOp}
          gameAlive={this.state.gameAlive}
          qna={this.state.questionsAndAnswers}
        />

        <Question
          qna={this.state.questionsAndAnswers}
          questionNum={this.state.questionNumber}
          pressedLettersArray={this.state.pressedLettersArray}
          gameAlive={this.state.gameAlive}
        />

        <GameStatus
          qna={this.state.questionsAndAnswers}
          gameAlive={this.state.gameAlive}
          resetGame={this.resetGame}
          questionNumber={this.state.questionNumber}
          gameCompleted={this.state.gameCompleted}
        />

        <Keyboard
          keys={this.state.keys}
          handlePressedLetter={this.handlePressedLetter}
          word={word}
          gameAlive={this.state.gameAlive}
          gameCompleted={this.state.gameCompleted}
        />
      </div>
    );
  }
}

export default Game;


When I console log to see if I fetched the data correctly it shows me undefined, but when I am using useEffect in functional component using the same url, I am able to see the data. But let's not divert from the motive of using it in the class based component.

The Error looks like: Error SS

The error is shown in the question class which looks like:

import React from 'react'
import { useTransition, animated } from 'react-spring'
import Letter from '../Letter/Letter'
import './Question.css'

const Question = ({gameAlive, qna, questionNum, pressedLettersArray}) => {
  const currentQ = qna[questionNum];

  const transitions = useTransition(currentQ, currentQ => currentQ.answer, {
    from: { opacity: 0, transform: 'translateX(100%)' },
    enter: { opacity: 1, transform: 'translateX(0px)' },
    leave: { opacity: 0, transform: 'translateX(-100%)' }
  })
  
  return (
    transitions.map(({ item, key, props }) => (
        <animated.div className={gameAlive ? "question" : "question gray"} key={key} style={props}>
          <p>{item.question}</p>
          <div className="guessing-word">
            {item.answer.split("").map((singleLetter, index) => (
              <Letter 
                key={`${index}${singleLetter}`}
                singleLetter={singleLetter}
                pressedLettersArray={pressedLettersArray}
              />
            ))}
          </div>
        </animated.div>
      )
    )
  )
}

export default Question


I know its a long description but I tried to go through all the resource available but yet not able to spot what I am missing out. Please help me resolve this. Thanks in advance.

result.json() returns a promise. Therefore changing your fetch to this may help

  fetch("http://localhost:8080/qna/all")
        .then(result=>result.json().then((jsonResult)=>{
            this.setState({questionsAndAnswers: jsonResult})
            console.log("Recieved Details")
        }))

I did not format the code properly because I want to highlight that .then() needs to be chained to the json call. In your code you have ((result) => result.json()).then() , so you need to remove the closing bracket ) and chain the .then() to .json() . Which would be ((result) => result.json().then(jsonRes => // do stuff))

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