McLean
McLean

Reputation: 1

Second fetch on API - React

I'm trying to access the data of an api and I have a problem that I can't solve...

I have made a call to the api and it returns the results correctly:

    export const getCharacters = async (category) => {
    
    const url = `https://www.breakingbadapi.com/api/characters?name=${encodeURI(category)}`;
    const resp = await fetch(url);
    const data = await resp.json();
  
    const characters = data.map(item => {
      return {
          id: item.char_id,
          title: item.name,
          url: item.img,
          nickname: item.nickname,
          bday: item.birthay,
          occupation: item.occupation,
          status: item.status 
      }
  })
    return characters;
  }

I put the results into a card and then I add in the character card a link to another component where I want to show "more details":


import React from 'react'
import { Link } from 'react-router-dom'

import '../../index.css'

export const CharactersGridItem = ({id, title, url}) => {


  return (
    <div className="card-global">
    <span className="col">
      <span className="card" style={{width: '10rem'}}>
        <span className="row">
          <img src={url} className="card-img-top" alt={title} style={{ width: '270px', height: '250px'}}></img>
          <h6 className="card-title">{title}</h6>
          <Link
          className="card-body"
            to={`/Characters/${id}`}
          >
            Details...
          </Link>
        </span>
      </span>
    </span>
  </div>

  )
}

All this works correctly and takes me to the url of the character's id on which I click.

My problem is when I try to recover those other properties of the character that, curiously, I can see them in the console (before everything explodes).

Here is the hook with which I made that second "request" for data:


import { useState, useEffect } from "react";

export const useCharacter = (id) => {

    const [character, setCharacter] = useState()

    function getCharacter(id) {
        return fetch(
          `https://www.breakingbadapi.com/api/characters/${encodeURI(id)}`
        );
    }

    useEffect(() => {
        // Después de que el componente se monta, se ejecuta este callback
        getCharacter(id).then((resp) => resp.json()).then(([body]) => {
            //console.log(body)
            setCharacter(body)
        })
    }, [])

    //return character;
    return character;

}

And this is where I bring the data:

import React from 'react'

import { useParams } from 'react-router-dom';
import { useCharacter } from '../../hooks/useCharacter';

export const CharactersDetail = (setCharacter) => {


  const { id } = useParams()


  const characters = useCharacter(id)

  console.log(characters)

 

  return (
    <div className="container">
    <div className="container-section">
    <p>{characters.name}</p>
    <p>{characters.nickname}</p>
    </div>
  </div>
  )

}

When I click on a character, and go to the details page (CharacterDetails), if I don't put this code on the return...:

   <p>{characters.name}</p>
<p>{characters.nickname}</p>

...everything works correctly and with console.log it prints the data json correctly (console). Even if I write the code and refresh the page (localhost:3000), it prints what I ask for.

But the moment I go back to the character page, click on another one (different id) and get to its corresponding detail page, everything explodes. An error tells me that it doesn't recognize the characters.name or characters.nickname.

Any ideas on why this might be happening? Any help is greatly appreciated!!!

Upvotes: 0

Views: 212

Answers (1)

Evert
Evert

Reputation: 99525

wcharacters will be undefined for the first render. Only after the data is received your components render again.

So you need to explicitly handle the case where useCharacter returns undefined.

For example:

export const CharactersDetail = (setCharacter) => {

  const { id } = useParams();

  const characters = useCharacter(id);
  if (!characters) {
    return <div>Loading...</div>;
  }

  return (
    <div className="container">
    <div className="container-section">
    <p>{characters.name}</p>
    <p>{characters.nickname}</p>
    </div>
  </div>
  )

}

Upvotes: 1

Related Questions