Search code examples
javascriptreactjstypescriptapiaxios

Issue getting API data to show in web browser using react.js


I am trying to create a trivia game using React.js Typescript and The Trivia API. I am transferring data through components using useContext, and am navigating through components using react-router-dom. When I move from the Category Spinning Wheel to the question, I am not seeing any text from the question. Any help would be appreciated!

This is the Spin Wheel component:

import React, {useState, useContext} from 'react';
import PlayerContext from '../context/PlayerContext';
import TriviaAPI from '../services/TriviaAPI';
import '../styles/CategorySpinWheel.css';
import {useNavigate} from "react-router-dom";

export default function CategorySpinWheel() {
    interface Category {
        id: number;
        name: string;
        class: string;
        uri: string
      }

      const navigate = useNavigate();

      const{currentQuestion, pastQuestions, score, hearts, difficulty, resetCurrentQuestion, pushPastQuestions, resetScore, addScore, resetHearts, deleteHeart, resetDifficulty} = useContext(PlayerContext);
      
      const categories: Category[] = [
        { id: 1, name: 'GENERAL KNOWLEDGE', class: "general-knowledge-spin", uri: "general_knowledge" },
        { id: 2, name: 'GEOGRAPHY', class: "geography-spin", uri: "geography" },
        { id: 3, name: 'FILM & TV', class: "film-tv-spin" , uri: "film_and_tv"},
        { id: 4, name: 'HISTORY', class: "history-spin", uri: "history" },
        { id: 5, name: 'MUSIC', class: "music-spin", uri: "music" },
        { id: 6, name: 'FOOD & DRINK', class: "food-drink-spin", uri: "food_and_drink" },
        { id: 7, name: 'SCIENCE', class: "science-spin", uri: "science" },
        { id: 8, name: 'ARTS & LITERATURE', class: "arts-literature-spin", uri: "arts_and_literature" },
        { id: 9, name: 'SPORT & LEISURE', class: "sport-leisure-spin", uri: "sport_and_leisure" },
        { id: 10, name: 'SOCIETY & CULTURE', class: "society-culture-spin", uri: "society_and_culture" }
      ];
      
    const [screen, setScreen] = useState("spin");

    const [spinResult, setSpinResult] = useState<Category | null>(null);
    const [isSpinning, setIsSpinning] = useState(false);
    const [randomCategoryClass, setRandomCategoryClass] = useState<string>("");
    const [spinButtonVisible, setSpinButtonVisible] = useState(true);

    const spinWheel = () => {
      setIsSpinning(true);
      const randomCategory = categories[Math.floor(Math.random() * categories.length)];
      TriviaAPI(randomCategory.uri, difficulty).then(data => {
        resetCurrentQuestion(data);
      })
      setRandomCategoryClass(randomCategory.class);
      setTimeout(() => {
          setSpinResult(randomCategory);
          setIsSpinning(false);
          setScreen("chosen");
      }, 6000);
  };
  return (
    <div>
      <div>
        <div>
            <p>Hearts</p>
            <p>Score: 200</p>
        </div>
        <h2>CATEGORY SPIN WHEEL</h2>
        <div id="wheel-div">
          <img src={process.env.PUBLIC_URL + '/images/wheel.png'} alt="spinning image" className={`${randomCategoryClass} wheel`} />
          <img src={process.env.PUBLIC_URL + '/images/WheelArrow.png'} alt="spinning image" className='wheel-arrow'/>
        </div>
        {
          spinButtonVisible && 
          <button onClick={() => {
            setSpinButtonVisible(false);
            spinWheel();
          }
          }>SPIN</button>
        }
      </div>
      {
        screen === "chosen" &&
        <div className='picked-screen'>
          <p>Your category is:</p>
          <p>{spinResult?.name}</p>
          <button onClick={() => {
            setScreen("spin");
            setRandomCategoryClass("");
            setSpinButtonVisible(true);
            navigate('/question');
          }}>GO TO QUESTION</button>
        </div>
      }
    </div>
  )
}

This is the question component:

import React, {useContext} from 'react'
import PlayerContext from '../context/PlayerContext'

export default function Question() {
    const{currentQuestion, pastQuestions, score, hearts, difficulty, resetCurrentQuestion, pushPastQuestions, resetScore, addScore, resetHearts, deleteHeart, resetDifficulty} = useContext(PlayerContext);
    let wrongAnswers = currentQuestion?.incorrectAnswers;
    let answers = [currentQuestion?.correctAnswer];
    for (let i = answers.length - 1; i > 0; i--){
        const j = Math.floor(Math.random() * (i + 1));
        [answers[i], answers[j]] = [answers[j], answers[i]];
    }
  return (
    <div>
        <h1>{currentQuestion?.category}</h1>
        <p>{currentQuestion?.question}</p>
        {answers.map(answer => 
            <button>{answer}</button>
        )}
    </div>
  )
}

this is the context:

import { createContext } from "react";

export interface Question{
    "category": string,
    "id": string,
    "correctAnswer": string,
    "incorrectAnswers": string[],
    "question": string,
    "difficulty": string
}

export interface PlayerContextModel{
    currentQuestion: Question | null;
    pastQuestions: Question[];
    score: number,
    hearts: number,
    difficulty: string,
    resetCurrentQuestion: (question: Question) => void,
    pushPastQuestions: (question: Question) => void,
    resetScore: () => void,
    addScore: () => void,
    resetHearts: () => void,
    deleteHeart: () => void,
    resetDifficulty: (diff: string) => void
}

const defaultValue: PlayerContextModel = {
    currentQuestion: null,
    pastQuestions: [],
    score: 0,
    hearts: 0,
    difficulty: "easy",
    resetCurrentQuestion: () => {},
    pushPastQuestions: () => {},
    resetScore: () => {},
    addScore:() => {},
    resetHearts:() => {},
    deleteHeart:() => {},
    resetDifficulty:() => {}
}

const PlayerContext = createContext(defaultValue);
export default PlayerContext;

This is the context provider:

import React, {ReactNode, useState, useContext} from 'react'
import { Question } from './PlayerContext';
import PlayerContext from './PlayerContext';

interface Props {children: ReactNode; }

export default function PlayerContextProvider({children}: Props) {
    const [currentQuestion, setCurrentQuestion] = useState<Question | null>(null);
    const [pastQuestions, setPastQuestions] = useState<Question[]>([])
    const [score, setScore] = useState<number>(0);
    const [hearts, setHearts] = useState<number>(0);
    const [difficulty, setDifficulty] = useState<string>("easy");
    function resetCurrentQuestion(question: Question): void {
        setCurrentQuestion(question)
    }
    function pushPastQuestions(question: Question): void {
        setPastQuestions(prev => [...prev, question]);
    }
    function resetScore(): void {
        setScore(0);
    }
    function addScore(): void {
        setScore(score + 100);
    }
    function resetHearts(): void {
        setHearts(0);
    }
    function deleteHeart(): void {
        setHearts(hearts - 1);
    }
    function resetDifficulty(diff: string): void {
        setDifficulty(diff);
    }
  return (
    <PlayerContext.Provider value={{currentQuestion, pastQuestions, score, hearts, difficulty, resetCurrentQuestion, pushPastQuestions, resetScore, addScore, resetHearts, deleteHeart, resetDifficulty}}>
        {children}
    </PlayerContext.Provider>
  )
}

And this is the service I am using to get the API Data:

import axios from "axios";
import { Question } from "../context/PlayerContext";

export default function TriviaAPI(category: string, difficulty: string): Promise<Question> {
  return axios
    .get<Question>(`https://the-trivia-api.com/api/questions?categories=${category}&limit=1&difficulty=${difficulty}`)
    .then((res) => {
      return res.data;
    })
}

Solution

  • res.data is an array, use res.data[0] to get the (first) question itself.