0

I thought had a better grasp of hooks but I've clearly got something wrong here. Not all of the character objects will have what I'm trying to get but it wont work with those that do.

I cna't even build in a check for character.comics.available. Same errors appear. I'm presuming I'm getting them before the state is set? But {character.name} always works.

CharacterDetail.js

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

const CharacterDetail = () => {
  // from the Route path="/character/:id"
  const { id } = useParams();
  // custom hook. useCharacter.js
  const [character] = useCharacter(id);
  // this only works sometimes but errors if i refresh the page
  // console.log(character.comics.available);
  return (
    <div>
      <h2 className="ui header">Character Details</h2>
      <p>Works every time: {character.name}</p>
      <div className="ui segment"></div>
      <pre></pre>
    </div>
  );
};

export default CharacterDetail;

Custom hook useCharacter.js

import { useState, useEffect } from "react";
import marvel from "../apis/marvel";

const useCharacter = (id) => {
  const [character, setCharacter] = useState({});

  useEffect(() => {
    loadItem();
    return () => {};
  }, [id]);

  const loadItem = async (term) => {
    const response = await marvel.get(`/characters/${id}`);
    console.log(response.data.data.results[0]);
    setCharacter(response.data.data.results[0]);
  };

  return [character];
};

export default useCharacter;

error when console is uncommented

Uncaught TypeError: Cannot read property 'available' of undefined
    at CharacterDetail (CharacterDetail.js:11)
... 

Here is the character object. enter image description here

skyboyer
  • 22,209
  • 7
  • 57
  • 64
v3nt
  • 2,845
  • 6
  • 36
  • 50
  • 1
    Your data is loading asynchronously, meaning it will not be available right away. Before it is loaded you will have empty object as character, so {}.comics.available - will give you error. But {}.name - will not give you error, it will give you "undefined", which is still not valid, but no errors. You have to check if object is not empty, before logging that. – Nikita Chayka Dec 14 '20 at 19:35
  • thanks. I'm aware of the data having to load before its available but I thought it was as character.nam is always populating? Its does come in a few ms after but doesn't through an error. – v3nt Dec 14 '20 at 20:01
  • .name access would not through an error, but will show you "undefined", you would try to do anything with that for example character.name.indexOf('x') - you would have an error – Nikita Chayka Dec 14 '20 at 20:02
  • thanks Nikita but tor's not the case. I never get an undefined from character.name. I always get the name, ie "agent Brand" – v3nt Dec 14 '20 at 20:07
  • just try to do character.name.indexOf('x') - and you will see error. You can read this also - https://stackoverflow.com/questions/23392111/console-log-async-or-sync to stop trusting console.log – Nikita Chayka Dec 14 '20 at 20:13
  • 1
    Oh and you are using .name in render() - so it will be called when it will actually be rendered, which is not right away, by that time - you may actually have the data (but you actually just getting lucky all the time). But you do console.log() before render - which means you can get less lucky :) try to do above (character.name.indexOf('x')) in console.log() before return (not in JSX) – Nikita Chayka Dec 14 '20 at 20:18

1 Answers1

0

thanks to @Nikita for the pointers. Settled on this...

CharacterDetail.js

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

const CharacterDetail = () => {
  const { id } = useParams();
  // custom hook. useCharacter.js
  const { isLoading, character } = useCharacter(id);
  const isArray = character instanceof Array;

  if (!isLoading && isArray === false) {
    console.log("isLoading", isArray);
    const thumb =
      character.thumbnail.path +
      "/portrait_uncanny." +
      character.thumbnail.extension;
    return (
      <div>
        <h2 className="ui header">{character.name}</h2>
        <img src={thumb} />
        <div className="ui segment">{character.comics.available}</div>
        <div className="ui segment">{character.series.available}</div>
        <div className="ui segment">{character.stories.available}</div>
      </div>
    );
  }

  return <div>Loading...</div>;
};

export default CharacterDetail;

useCharacter.js

import { useState, useEffect } from "react";
import marvel from "../apis/marvel";

function useCharacter(id) {
  const [character, setCharacter] = useState([]);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    setIsLoading(true);
    const fetchData = async () => {
      setIsLoading(true);
      await marvel
        .get(`/characters/${id}`)
        .then((response) => {
          /* DO STUFF WHEN THE CALLS SUCCEEDS */
          setIsLoading(false);
          setCharacter(response.data.data.results[0]);
        })
        .catch((e) => {
          /* HANDLE THE ERROR (e) */
        });
    };
    fetchData();
  }, [id]);
  return {
    isLoading,
    character,
  };
}

export default useCharacter;
v3nt
  • 2,845
  • 6
  • 36
  • 50