Home > Back-end >  Unable to display API data on React frontend
Unable to display API data on React frontend

Time:10-05

I'm trying to return data fetched from a private API and display it on a page. My frontend use React JS and my backend use node with Express and Axion. My code work up to the point of returning the data. I get my APi Key and fetch my data but the data is not transferred to my page (Quotes.js).

Backend app.js

import express from "express";
import { getCase } from "./getCase.js";

const app = express();

app.use(function (req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content-Type, Accept"
  );
  next();
});

app.get("/", function (req, res) {
  console.log("app.js call getCase");
  res.send(getCase());
  //console.log(req);
});

//console.log(Quote.getQuote());

let port = process.env.PORT;
if (port == null || port == "") {
  port = 5000;
}

app.listen(port, function () {
  console.log(`Server started on port ${port}...`);
});

Backend getCase

import { getToken } from "./nsApiToken.js";
import axios from "axios";

let getData = "";
console.log("begin of getCase");
const getCase = async () => {
  let tokenRes = await getToken();

  const url =
    "https://5156735-sb1.app.netsuite.com/app/site/hosting/restlet.nl?script=860&deploy=1&recordtype=supportcase&id=717986";

  try {
    const res = await axios.get(url, {
      headers: {
        Authorization: `Bearer ${tokenRes.data.access_token}`,
      },
    });
    return res;
  } catch (error) {
    return error;
  }
};

export { getCase };

Frontend App.js

import logo from "./logo.svg";
import "./App.css";
import Quotes from "./Quotes.js";

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <Quotes />
      </header>
    </div>
  );
}

export default App;

Frontend Quotes.js

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

const Quotes = async () => {
  const [text, setText] = useState([]);
  const [author, setAuthor] = useState("");

  const getQuote = await axios
    .get("http://localhost:5000", {
      crossdomain: true,
    })
    .then((res) => res.data)
    .then((data) => {
      setText({
        data: data,
      });
      console.log("res: ", text);
    });

  return (
    <div>
      <button onClick={getQuote}>Generate Quote</button>
      <h1>{text}</h1>
      <h3>{author}</h3>
    </div>
  );
};

export default Quotes;

Process: When I run my process the front execute and call Quotes.js in the axios get process. app.js then route to home ('/') and call getCase via the app.get. The getCase process execute get the API token and add it in the headers Authorization. The process initiate the call and fetch the data (if I console.log(res.data.fields.phone) or console.log(res.data.id) I see the correct data. In my Quotes.js I want to display the data but res.data is empty, yet I get back status 200.

I've been trying to understand why it is not passing the data from the backend to the frontend.

CodePudding user response:

There are several problems and some improvements to be made.

Backend

Problem - You are sending the entire AxiosResponse in the response from your Express app

Just send the data

const getCase = async () =>
  (
    await axios.get(
      "https://5156735-sb1.app.netsuite.com/app/site/hosting/restlet.nl",
      {
        params: {
          script: 860,
          deploy: 1,
          recordtype: "supportcase",
          id: 717986,
        },
        headers: {
          Authorization: `Bearer ${(await getToken()).data.access_token}`,
        },
      }
    )
  ).data; // Return the data, not the whole response

Problem - getCase() is async

You need to await the result

app.get("/", async (req, res, next) => {
  try {
    res.json(await getCase());
  } catch (err) {
    next(err); // send the error to the Express error handler
  }
});

Improvement - Creating your own CORS middleware is a waste of time

By the time you create a comprehensive CORS middleware, it will look exactly the same as the standard one so just use that

import express from "express";
import cors from "cors";

const app = express();
express.use(cors());

Frontend

Problem - React function components cannot be async

Function components must return a valid JSX node. Remove async from Quotes

Problem - getQuote should be a function

In order to trigger getQuote by button click, it needs to be a function

// if text is an object, initialise it as one
const [text, setText] = useState({});

const getQuotes = async () => {
  try {
    // there is no "crossdomain" Axios option
    const { data } = await axios.get("http://localhost:5000");

    setText({ data });
  } catch (err) {
    console.error(err.toJSON());
  }
};

Problem - the text state is an object

JSX cannot render plain objects, you instead need to reference properties that can be rendered.

<h1>{text.data?.some?.property}</h1>

No idea what your response object looks like so this is just generic advice

CodePudding user response:

There seems issue with code in Quotes component rendering,

You should add the API call under the lifecycle hook or method in the class component like useEffect or componentDidMount to avoid infinite render of components.

Like, File Quotes.js

import React, { useState, useEffect } from 'react';
import axios from 'axios';
    
const Quotes = async() => {
    const [text, setText] = useState([]);
    const [author, setAuthor] = useState('');

    useEffect(() => {
        axios.get('http://localhost:5000', { crossdomain: true })
            .then((res) => {
                setText(res.data);
            });
    }, [])
    
    
    return (
        <div>
            <button onClick={getQuote}>Generate Quote </button>
            <h1>{text}</h1>
            <h3>{' '   author}</h3>
        </div>
    );
};
    
export default Quotes

CodePudding user response:

The reason why this is not working is for two reasons. Firstly, res.data is not an asynchronous function. And since you are doing await, you can just get data. Secondly, you need to make your API calls and setState in the useEffect hook or else it would just end up in an infinite rerender situation. You just have to do the following and it should work:


useEffect(() => {
  const fetchData = async () => {
    const {data} = await axios
      .get('http://localhost:5000', {
        crossdomain: true
      })
     setText(data)
  }
  fetchData()
}, [])
 
  • Related