One fetch request creates many GET requests on server - javascript

Here is my code:
import React from "react";
import { useState, useEffect } from "react";
import TutorialList from "./TutorialList";
import PropTypes from "prop-types";
const Home = () => {
const [tutorials, setTutorials] = useState(null);
useEffect(() => {
console.log("Fetching data");
fetch("http://192.168.212.52:8080/api/tutorials/all/")
.then((res) => {
return res.json();
console.log("Getting json from response");
})
.then((data) => {
console.log(data);
setTutorials(data);
});
}, []);
return (
<div className="home">
{console.log(tutorials)}
{tutorials && (
<TutorialList tutorials={tutorials} title={"All tutorials"} />
)}
</div>
);
};
Home.propTypes = {
title: PropTypes.string,
};
export default Home;
I expect this to make 1 get request to the server, which returns the data
that is then rendered with react.
What it actually does is make more than 10
requests to the server, still rendering the data after all the requests
finish. I can see the requests both from the server logs and from the browser
networking dev tools.
However, the Fetching data and Getting json from response logs only execute
once, as expected.

Since fetch requests a resource from the network, it runs asynchronously. This said, if you want to get to run it inside a useEffect it might be that if you wrap it in an async function it should work. However, keep in mind that it's not the argument of this hook itself async but another function that you define inside. For instance,
useEffect(() => {
console.log("Fetching data");
async function retrieveData() {
const json = await fetch("http://192.168.212.52:8080/api/tutorials/all/")
const data = await response.json()
setTutorials(data)
console.log("Got json from response");
}
retrieveData()
}, []);

Related

React Props to Pass Large Set of Object

I have a scenario where i want to first make a request and validate the response from the server and then show UI. Complete Response also needs to be passed in the component. I have sample code and i got the proper response from server but it does not get passed to the component. I am using props to pass data to Component. How can i achieve this ?
index.tsx
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
App.tsx
import React from "react";
import logo from "./logo.svg";
import "./App.css";
import { CarsGrid } from "./CarsGrid";
const init = async () => {
try {
const response = await fetch(
"https://www.ag-grid.com/example-assets/row-data.json"
)
return await response.json();
} catch (err) {}
};
function App() {
init().then(result=> {
console.log('Correct Response is printed',result);
return <CarsGrid gridRows={result} />;
});
return (<></>)
}
export default App;
React Component
export function CarsGrid({gridRows}) {
console.log('Data gridRows', gridRows);
})
But results does not get printed in console.log('Data gridRows', gridRows); though response is printed at console.log('Correct Response is printed',result);
Any help is appreciated.
The return in your code:
init().then(result=> {
console.log('Correct Response is printed',result);
return <CarsGrid gridRows={result} />;
});
is returning for the callback passed to then, not as the rendering result of your App function/component.
So the init().then() part is not contributing to the rendering process. This is as if your App code was:
function App() {
return (<></>)
}
And so your CarsGrid function is never executed.
A "react" way would be to rely on a state and to fetch your data in an effect:
function App() {
const [data, setData] = useState();
useEffect(() => {
const init = async () => {
try {
const response = await fetch(
"https://www.ag-grid.com/example-assets/row-data.json"
);
const result = await response.json();
console.log('Correct Response is printed',result);
setData(result);
} catch (err) {}
};
init();
}, []);
return data && <CarsGrid gridRows={data} />;
}

React does not wait for server call to complete and throws error

I have a working rest service that I want to invoke in react.
The code does display the list of countries.
import {React, useEffect, useState } from 'react'
export const Noc = () => {
const [nocs, setNoc] = useState();
useEffect(
() => {
const fetchNoc = async () => {
const response = await fetch('http://localhost:8080/countrydefinitions');
const data = await response.json();
setNoc(data);
};
fetchNoc();
},[]
);
return <div>
<div>NOC LIST</div>
{nocs.map(noc => <div>{noc.region}</div>)}
</div>
}
But most of the times I get this error
TypeError: Cannot read property 'map' of undefined
sometimes it prints the list soemetimes it does'nt. Is there some sort of a delay or wait that I need to introduce?
How can I introduce a delay or make sure that setnoc has been called and nocs has a value before printing it.
React will rerender when the props change. Indeed at the beginning its nothing yet.
2 things to make it more solid can be done.
Add a default value, so that map is available on the array.
const [nocs, setNoc] = useState([]);
And/Or wait until noc is not undefined anymore, and validating that It has a map function, before trying to use map.
{nocs && typeof nocs.map === 'function' && nocs.map(noc => <div>{noc.region}</div>)}
No. there is no need to introduce any delay/wait. That is already handled by the async/await syntax.
You can either set an initial value in your useState or early return a custom message if nocs is undefined.
If you are fetching from your own api, you can return a response with an error if the fetch request should fail. And then at client side, you can handle that error by wrapping your fetch call inside a try-catch block
import {useEffect, useState } from 'react'
export const Noc = () => {
const [nocs, setNoc] = useState();
useEffect(
() => {
const fetchNoc = async () => {
const response = await fetch('http://localhost:8080/countrydefinitions');
const data = await response.json();
setNoc(data);
};
try{
fetch()
}catch(error){
//handle error here
console.log(error);
}
},[]
);
if(!nocs){
return (<p>No NOC found</p>)
}
return <div>
<div>NOC LIST</div>
{nocs.map(noc => <div>{noc.region}</div>)}
</div>
}
at first render, nocs has no data then useEffect runs and the second render, nocs will have data.
you need to check if nocs is undefined
{nocs && nocs.length > 0 && nocs.map(noc => <div>{noc.region}</div>)}

getServerSideProps functions response cannot be serialized as JSON in Next.js

I am building a Next.js application with multiple pages with dynamic routing. Each page has multiple axios calls to the backend that are called with useEffect. My goal is to instead call these functions with getServerSideProps functions for speed purposes as the application is scaled to accomodate a larger user database.
My issue is when i try to recieve emails from the database, I get the error:
Error: Error serializing .allEmails.config.transformRequest[0] returned from getServerSideProps in "/emails".
Reason: function cannot be serialized as JSON. Please only return JSON serializable data types.
I want to recieve emails and pass it into props where i can then access the data on the page.
import React, { useState, useEffect, useContext } from 'react';
import axios from 'axios';
import jsHttpCookie from 'cookie';
import jsCookie from 'js-cookie';
const Emails = ({allEmails}) => {
const [emails, setEmails] = useState(allEmails);
return (
<></>
)
}
export async function getServerSideProps({req, res}) {
const {token} = jsHttpCookie.parse(req.headers.cookie);
const allEmails = await axios.get("http://localhost:8000/api/allCompanyEmails");
console.log(allEmails, "all data")
return {
props: {
allEmails
}
}
}
export default Emails;
allEmails is actually AxiosResponse type, it is not the data you get from api. And it contains non-serializable stuff like functions and etc.
To get the data you need to access data property of this response:
export async function getServerSideProps({req, res}) {
const {token} = jsHttpCookie.parse(req.headers.cookie);
const response = await axios.get("http://localhost:8000/api/allCompanyEmails");
console.log(response, "all data")
return {
props: {
allEmails: response.data
}
}
}

Waiting for async function in React component & Showing Spinner

Beginner here.
Trying to fetch some data from a server and display it in my react component once its fetched.
However, I am having trouble integrating the async function into my react component.
import React, { useState } from "react";
import { request } from "graphql-request";
async function fetchData() {
const endpoint = "https://localhost:3090/graphql"
const query = `
query getItems($id: ID) {
item(id: $id) {
title
}
}
`;
const variables = {
id: "123123123"
};
const data = await request(endpoint, query, variables);
// console.log(JSON.stringify(data, undefined, 2));
return data;
}
const TestingGraphQL = () => {
const data = fetchData().catch((error) => console.error(error));
return (
<div>
{data.item.title}
</div>
);
};
export default TestingGraphQL;
I'd like to simply show a spinner or something while waiting, but I tried this & it seems because a promise is returned I cannot do this.
Here you would need to use the useEffect hook to call the API.
The data returned from the API, I am storing here in a state, as well as a loading state to indicate when the call is being made.
Follow along the comments added in between the code below -
CODE
import React, { useState, useEffect } from "react"; // importing useEffect here
import Layout from "#layouts/default";
import ContentContainer from "#components/ContentContainer";
import { request } from "graphql-request";
async function fetchData() {
const endpoint = "https://localhost:3090/graphql"
const query = `
query getItems($id: ID) {
item(id: $id) {
title
}
}
`;
const variables = {
id: "123123123"
};
const data = await request(endpoint, query, variables);
// console.log(JSON.stringify(data, undefined, 2));
return data;
}
const TestingGraphQL = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
// useEffect with an empty dependency array works the same way as componentDidMount
useEffect(async () => {
try {
// set loading to true before calling API
setLoading(true);
const data = await fetchData();
setData(data);
// switch loading to false after fetch is complete
setLoading(false);
} catch (error) {
// add error handling here
setLoading(false);
console.log(error);
}
}, []);
// return a Spinner when loading is true
if(loading) return (
<span>Loading</span>
);
// data will be null when fetch call fails
if (!data) return (
<span>Data not available</span>
);
// when data is available, title is shown
return (
<Layout>
{data.item.title}
</Layout>
);
};
since fetchData() returns a promise you need to handle it in TestingGraphQL. I recommend onComponentMount do your data call. Setting the data retrieved into the state var, for react to keep track of and re-rendering when your data call is finished.
I added a loading state var. If loading is true, then it shows 'loading' otherwise it shows the data. You can go about changing those to components later to suit your needs.
See the example below, switched from hooks to a class, but you should be able to make it work! :)
class TestingGraphQL extends Component {
constructor() {
super();
this.state = { data: {}, loading: true};
}
//when the component is added to the screen. fetch data
componentDidMount() {
fetchData()
.then(json => { this.setState({ data: json, loading: false }) })
.catch(error => console.error(error));
}
render() {
return (
{this.state.loading ? <div>Loading Spinner here</div> : <div>{this.state.data.item.title}</div>}
);
}
};

Handling promises with fetch() in React?

I'm creating my first MERN stack application, and trying to implement a simple API that calls my express server from my React front-end components. I have the API working on the back end, and it is sending the data correctly through fetch(), but I'm having trouble resolving the promise from fetch() in my React component, with the call not stopping firing. My code looks as follows (assuming as of right now all API calls return a dummy format like { title: 'foo', ... }:
import React, { useState } from 'react';
import 'core-js/stable';
import 'regenerator-runtime/runtime';
const getApiData = async (route) => {
try {
let apiData = await fetch(route);
let apiDataJson = await apiData.json();
return apiDataJson;
} catch (err) {
throw new Error('Error on fetch', {
error: err
})
}
}
var retrieve_data = async (route, setterCallback) => {
await getApiData(`/api/${route}`).then((data) => {
console.log('Data retrieved from API')
setterCallback(<div>{data.title}</div>)
}).catch(() => {
setterCallback(<div>ERROR</div>)
})
}
const MyComponent = () => {
const [innerDiv, setinnerDiv] = useState(0);
let data = retrieve_data('myEndpoint', setinnerDiv);
return(
<div>
<h1>Data Retrieved in MyComponent:</h1>
{innerDiv}
</div>
);
}
When I compile the above the component successfully renders (i.e. <MyComponent /> looks like:
<div>
<h1>Data Retrieved in MyComponent:</h1>
<div>foo</div>
</div>
However, then then block keeps executing (i.e. the 'Data retrieved from API' logs to the console hundreds of times/second until I close the application. How can I stop this from executing once it has set the component? Thanks!
You need to useEffect to stop the component from re-rendering. Try something like this.
const MyComponent = () => {
const [innerDiv, setinnerDiv] = useState(0);
useEffect(() => {
retrieve_data('myEndpoint', setinnerDiv);
}, []);
return(
<div>
<h1>Data Retrieved in MyComponent:</h1>
{innerDiv}
</div>
);
}

Categories