zadders
zadders

Reputation: 438

How to use a state from one class to another

So i'm currently working on a PokeDex using the PokeApi available online.

The code of the project is as follows:

import React, { Component } from "react";
import PokemonCard from "./PokemonCard";
import "../ui/PokemonList.css";
import axios from "axios";

export const PokemonList = class PokemonList extends Component {
  state = {
    url: "https://pokeapi.co/api/v2/pokemon/",
    pokemon: null
  };

  async componentDidMount() {
    const res = await axios.get(this.state.url);
    this.setState({ pokemon: res.data["results"] });

    console.log(res);
  }

  render() {
    return <div></div>;
  }
};
export const PokeList = () => {
  return (
    <React.Fragment>
      {this.state.pokemon ? (
        <section className="poke-list">
          {this.state.pokemon.map(pokemon => (
            <PokemonCard />
          ))}
        </section>
      ) : (
        <h1>Loading Pokemon</h1>
      )}
    </React.Fragment>
  );
};

As you can see, I have declared a state in the PokemonList Component class, but then I try to call it further down within the variable PokeList. The issue is that the state is not being recognized in PokeList

(I get the error "TypeError: Cannot read property 'state' of undefined" )

How can I go about calling the state that's declared in the class above?

-------------------EDIT-------------------------------

Okay, so I realized something. I have a code for my Dashboard.js that displays my list. Code is as follows

import React, { Component } from "react";

import { PokeList } from "../pokemon/PokemonList";

export default class Dashboard extends Component {
  render() {
    return (
      <div>
        <div className="row">
          <div className="col">
            <PokeList />
          </div>
        </div>
      </div>
    );
  }
}

When I change the code from PokeList to PokemonList. so it'd be

import React, { Component } from "react";

import { PokemonList } from "../pokemon/PokemonList";

export default class Dashboard extends Component {
  render() {
    return (
      <div>
        <div className="row">
          <div className="col">
            <PokemonList />
          </div>
        </div>
      </div>
    );
  }
}

I think get a list of 20 pokemon from the Api from

console.log(this.state.pokemon);.

But since I'm not displaying PokeList on the dashboard, then none of the pokemon cards display.

Screenshot of console output

enter image description here

Upvotes: 0

Views: 127

Answers (4)

Elder
Elder

Reputation: 1514

There's a little of confusion between your PokemonList and PokeList component. I believe that what you really are looking for is to have just one of those. If you mix the two, you can have a component that controls the view based on the state, in your case, the state is your Pokemon list.

I mixed the two here, so your render method renders "Loading Pokemon" until you get your response back from axios, then when the response is back, it gets that data, updates your state and the state update trigger a re-render.

import React, { Component } from "react";
import PokemonCard from "./PokemonCard";
import axios from "axios";

class PokemonList extends Component {
  state = {
    url: "https://pokeapi.co/api/v2/pokemon/",
    pokemon: null
  };

  componentDidMount() {
    axios.get(this.state.url).then(res => {
      this.setState({ pokemon: res.data["results"] });
    });
  }

  render() {
    let pokemonList = <h1>Loading Pokemon</h1>;

    const pokemons = this.state.pokemon;

    if (pokemons) {
      pokemonList = (
        <section className="poke-list">
          <ul>
            {pokemons.map(pokemon => (
              <PokemonCard pokemon={pokemon} />
            ))}
          </ul>
        </section>
      );
    }

    return <React.Fragment>{pokemonList}</React.Fragment>;
  }
}

export default PokemonList;

I also created a simple PokemonCard component where I list the result from the API, just to show you that that approach works.

import React from "react";

const pokemonCard = props => {
  return (
    <li key={props.pokemon.name}>
      <a href={props.pokemon.url}>{props.pokemon.name}</a>
    </li>
  );
};

export default pokemonCard;

You can find the final code, with PokeList and PokemonList now combined into one component called PokemonList here:

Edit shy-pine-de969

Keep in mind that if your render function depends on a certain state, it's probably certain that you should have that state being managed in that component, or passed down from a parent component.

In your example, I noticed you set url inside your state. URL is really not something that will change. It's a constant,so you can easily remove that from your state and place it in a variable and just leave your pokemon list there.

For example:

const url = "https://pokeapi.co/api/v2/pokemon/";
state = {
    pokemon: null
  };

  componentDidMount() {
    axios.get(url).then(res => {
      this.setState({ pokemon: res.data["results"] });
    });
  }

Upvotes: 1

Brian Polanco
Brian Polanco

Reputation: 48

You need iterate your your pokelist passing the result from your componentDidMount function to your child component as a prop , then receive your prop in the child component here it's a working codesandbox iterating your pokemon names in the pokeList child component

Upvotes: 0

Prakash Karena
Prakash Karena

Reputation: 2605

import  React , { Component } from "react";
import axios from "axios";
//make it as class based component
export default class PokemonList extends Component {
  state = {
    url: "https://pokeapi.co/api/v2/pokemon/",
    pokemon: null
  };

  async componentDidMount() {
    const res = await axios.get(this.state.url);
    this.setState({ pokemon: res.data["results"] });

    console.log(res);
  }

  render() {
    //check your data here 
     console.log(this.state.pokemon)
    {/*pass data to child*/}
    return <div> <PokeList data = { this.state } /> </div>;
  }
};

//export this component
export const PokeList = (props) => {
//check your data is coming or not
console.log(props.data)
//access your data from props
  return (
    <React.Fragment>
      {props.data.pokemon ? (
        <section className="poke-list">
          {props.data.pokemon.map(pokemon => (
               pokemon.name
          ))}
        </section>
      ) : (
        <h1>Loading Pokemon</h1>
      )}
    </React.Fragment>
  );
};

Upvotes: 0

thuva4
thuva4

Reputation: 1225

First of all functional components are stateless. If you need to maintain state use class components or hooks. You can't use the state of one component in another component, You have two options,

  1. Create a parent-child relationship between those components
  2. Use state management libraries(Redux, etc)

Upvotes: 2

Related Questions