Search code examples
reactjsapiundefined

Undefined parameter in react function when retrieving data from API


I am trying to retrieve data from an API, however it requires an encrypted name, which I receive from another API call. So, I am trying to use the data from the first API to retrieve the Data in the Second API, however when I try to pass in the appropriate data, it says that it is undefined, when I can even print it out.

import { useState, useEffect } from "react";
import { Typography, Row, Col, Statistic } from "antd";
import { Link } from "react-router-dom";
import axios from "axios";

const { Title } = Typography;

const Homepage = () => {
  const [searchText, setSearchText] = useState("");
  const [playerData, setPlayerData] = useState({});
  const [playerStats, setPlayerStats] = useState({});

  const API_KEY = "MyprivateAPIKEY";

  function searchForPlayer(event) {
    var APICallSummoner =
      "https://na1.api.riotgames.com/lol/summoner/v4/summoners/by-name/" +
      searchText +
      "?api_key=" +
      API_KEY;

    axios
      .get(APICallSummoner)
      .then(function (response) {
        setPlayerData(response.data);
      })
      .catch(function (error) {
        console.log(error);
      });
  }

  function searchPlayerData(id) {
    var API =
      "https://na1.api.riotgames.com/lol/league/v4/entries/by-summoner/" +
      id +
      "?api_key=" +
      API_KEY;
    axios
      .get(API)
      .then(function (response) {
        setPlayerStats(response.data);
      })
      .catch(function (error) {
        console.log(error);
      });
  }

  return (
    <>
      <Title level={2} className='heading'>
        LoLTracker
      </Title>

      <input
        type='text'
        onChange={(e) => setSearchText(e.target.value)}></input>
      <button
        onClick={(e) => {
          searchForPlayer(e);
          var a = playerData.id;
          console.log(a);
          searchPlayerData(a);
        }}>
        Search Player
      </button>

      {JSON.stringify(playerData) != "{}" ? (
        <>
          <p>{playerData.name}</p>
          <img
            width='100'
            height='100'
            src={
              "http://ddragon.leagueoflegends.com/cdn/12.3.1/img/profileicon/" +
              playerData.profileIconId +
              ".png"
            }></img>
          <p>Summoner Level: {playerData.summonerLevel} </p>
        </>
      ) : (
        <>
          <p>No Player Data</p>
        </>
      )}

      <Row>
        <Col span={12}>
          <Statistic title='Total Games Played' value={playerStats.wins} />
        </Col>
        <Col span={12}>
          <Statistic title='Ranked Solo Duo Games' value='5' />
        </Col>
        <Col span={12}>
          <Statistic title='Wins' value='5' />
        </Col>
        <Col span={12}>
          <Statistic title='Losses' value='5' />
        </Col>
      </Row>
    </>
  );
};

export default Homepage;

In my onClick() function, I get the required information from searchForPlayer, but i cannot pass the data to my searchForPlayer function. Pls help


Solution

  • In React state updates are asynchronous in nature, so because of this you cannot expect updated state values immediately on the next line after calling state update(setState).
    In your case on button click, you are doing this -

    <button
        onClick={(e) => {
            // this will change state value for playerData
            searchForPlayer(e);
            // but here on next line you cannot access updated value
            // because searchForPlayer will not run synchronously
            var a = playerData.id;
            console.log(a); // so you will mostly get undefined here
            searchPlayerData(a);
        }}>
        Search Player
    </button>
    
    1. To fix this either you need to use useEffect with dependency array with playerData like this -
    React.useEffect(() => {
        if(playerData.id){
            //call next function or api here
        }
    }, [playerData])
    

    Above useEffect will get called when your playerData gets changed. So in here, you can call anything which needs state playerData.

    1. Or if you only need to call searchPlayerData after searchForPlayer then instead of using the state to store playerData and then passing it to searchPlayerData you can directly call searchPlayerData inside .then in searchForPlayer, see below code -
    function searchForPlayer(event) {
        var APICallSummoner =
            "https://na1.api.riotgames.com/lol/summoner/v4/summoners/by-name/" +
            searchText +
            "?api_key=" +
            API_KEY;
    
        axios
            .get(APICallSummoner)
            .then(function (response) {
                // you do not need to set below state value
                // if you are not going to use it other than calling next api
                setPlayerData(response.data);
                if(response.data){
                    //calling next function here
                    searchPlayerData(response.data.id);
                }
            })
            .catch(function (error) {
                console.log(error);
            });
    }