Search code examples
javascriptreactjsaxiosreact-hookses6-promise

Cant access object value returned from axios in custom hooks


Hi I'm writing code to get JSON with axios in custom hooks and return the value, but I can't access the object property even it was there.

I have been trying to access like item.title or item["title"] and even JSON.parse() but parse() returned cross origin error.

customHooks.js

import { useState, useEffect } from "react";
import axios from "axios";

const GetPost = (id) => {
  const [post, setPost] = useState();
  useEffect(() => {
    axios.get(`http://localhost:8000/post/${id}`).then((res) => setPost(res));
  }, []);

  return post;
};

export default GetPost;

Function where hook is called and I want to access the object:

import React from "react";
import getPost from "../customHooks/GetPost";

import { Jumbotron, Container } from "react-bootstrap";

const PostDetail = (props) => {
  const item = getPost(props.match.params.id);

  console.log(item); // i can access the object
  console.log(item.title); //TypeError: Cannot read property 'title' of undefined
  return (
    <Jumbotron fluid>
      <Container>
        {/* <h1>{item.title}</h1>
        <p>{item.description}</p> */}
      </Container>
    </Jumbotron>
  );
};

export default PostDetail;

The actual response from server http://localhost:8000/post/post_5e9cbf07b44c7 is:

{
    "message": "success",
    "item": {
        "post_id": "post_5e9cbf07b44c7",
        "title": "Asperiores.",
        "description": "Porro iure aliquam laborum veniam quis vel. At itaque voluptas voluptas natus eligendi aut facilis.",
        "content": "lorem ipsum",
        "img_url": "https://lorempixel.com/100/100/cats/?61693",
        "created_by": "user_5e9cbf07b48fc",
        "created_at": "2020-04-19 21:13:43",
        "updated_at": "2020-04-19 21:13:43"
    }
}

The value of console.log(item) in PostDetail.js is:

{
  "data": {
    "message": "success",
    "item": {
      "post_id": "post_5e9cbf07b44c7",
      "title": "Asperiores.",
      "description": "Porro iure aliquam laborum veniam quis vel. At itaque voluptas voluptas natus eligendi aut facilis.",
      "content": "lorem ipsum.",
      "img_url": "https://lorempixel.com/100/100/cats/?61693",
      "created_by": "user_5e9cbf07b48fc",
      "created_at": "2020-04-19 21:13:43",
      "updated_at": "2020-04-19 21:13:43"
    }
  },
  "status": 200,
  "statusText": "OK",
  "headers": {
    "cache-control": "no-cache, private",
    "content-type": "application/json"
  },
  "config": {
    "url": "http://localhost:8000/post/post_5e9cbf07b44c7",
    "method": "get",
    "headers": {
      "Accept": "application/json, text/plain, */*"
    },
    "transformRequest": [
      null
    ],
    "transformResponse": [
      null
    ],
    "timeout": 0,
    "xsrfCookieName": "XSRF-TOKEN",
    "xsrfHeaderName": "X-XSRF-TOKEN",
    "maxContentLength": -1
  },
  "request": {}
}

thanks in advance :)


Solution

  • The problem happens when you try to access the returned value because on initial render the value is not available since the effect runs post render cycle and also because the axios request is async.

    It will however work if you actually initialize the state to object within your custom hook

    Also make sure that you set res.data.item in the state and not res. Post that you can get the value with item.title

    SideNotes:

    Please ensure that you prefix your custom hooks names with use which actually helps eslint figure out the distinction between custom hook or a function.

    Also since you are making an API call based on param id, I advice you to pass that id as a dependency to useEffect in custom hook so that the scenarios where id is changed also work seamlessly

    import { useState, useEffect } from "react";
    import axios from "axios";
    
    const useGetPost = (id) => {
      const [post, setPost] = useState({}); // initialize here
      useEffect(() => {
        axios.get(`http://localhost:8000/post/${id}`).then((res) => setPost(res.data.item));
      }, [id]); // pass id as a dependency here
    
      return post;
    };
    
    export default useGetPost;
    

    Use it in PostDetail component like

    import React from "react";
    import useGetPost from "../customHooks/GetPost";
    
    import { Jumbotron, Container } from "react-bootstrap";
    
    const PostDetail = (props) => {
      const item = useGetPost(props.match.params.id);
    
      console.log(item); 
      console.log(item.title);
      return (
        <Jumbotron fluid>
          <Container>
            {/* <h1>{item.title}</h1>
            <p>{item.description}</p> */}
          </Container>
        </Jumbotron>
      );
    };
    
    export default PostDetail;