reactjsjsontypescriptapiaxios

React TS Axios using local JSON api


I am trying to connect a test JSON server to my frontend react using axios. I see the objects in the Network inspect, but nothing displays to the site.

I'm thinking it may be something with the mapping? when I removed ? I get an error in the console. "Uncaught TypeError: Cannot read properties of undefined (reading 'map')" The status code reads 200 and I see the preview and response. I'm super new to Typescript and React so I'm not really sure what the issue is. Thanks for understanding!

testapi

import axios from "axios";
export default axios.create({
  baseURL: "http://localhost:3000/",
  method: "GET",
});

useTest

import { CanceledError } from "axios";
import { useEffect, useState } from "react";
import testapi from "./api-client";

export interface Title {
  title_id: number;
  title_name: string;
  title_description: string;
}

interface FetchTitleResponse {
  count: number;
  results: Title[];
}

const useTest = () => {
  const [titles, setTitles] = useState<Title[]>([]);
  const [error, setError] = useState("");

  useEffect(() => {
    const controller = new AbortController();

    testapi
      .get<FetchTitleResponse>("/titles", { signal: controller.signal })
      .then((res) => setTitles(res.data.results))
      .catch((err) => {
        if (err instanceof CanceledError) return;
        setError(err.message);
      });
    return () => controller.abort();
  }, []);
  return { titles, error };
};

export default useTest;

app

import useTest from "./useTest";

function App() {
  const { titles, error } = useTest();

  return (
    <div>
      {error && <div>{error}</div>}
      hi
      <ul>
        {titles?.map((title) => (
          <li key={title.title_id}>{title.title_name}</li>
        ))}
      </ul>
    </div>
  );
}

export default App;

JSON example. I'm mainly just trying to see if I can connect to a JSON file before my group mate finishes the API

objects show in network preview, and heading is reading 200

response image, Note: on the JSON I changed the name from titles to results with no luck


Solution

  • Your code appears and runs correctly. Please make sure your response looks like this:

    // where res.data =>
    {
      count: number,
      results: [{
        title_id: number,
        title_name: string,
        title_description: string
      }]
    }
    

    Edit

    In regards to the following line,

    .then((res) => setTitles(res.data.results))
    

    The res.data is the complete response payload from GET request (an object). The subsequent access to results (object array) is a named property of the response object.

    Please review the example structure below. This is what the "Response" tab in Chrome should be displaying. It should not be a simple array of title objects.

    Example

    // GET http://localhost:3000/titles (response)
    {
      "count": 2,
      "results": [
        {
          "title_id": 1,
          "title_name": "Foo",
          "title_description": "Foo..."
        },
        {
          "title_id": 2,
          "title_name": "Bar",
          "title_description": "Bar..."
        }
      ]
    }
    

    The results should not be null. It looks like you are already guarding against null with optional chaining:

    <ul>
      {titles?.map((title) => (
        <li key={title.title_id}>{title.title_name}</li>
      ))}
    </ul>
    

    I suspect that your top-level results key is not actually an array.