React fetch stops working after page refresh - reactjs

I am fetching a single object. When the page loads for the first time the fetched elements does render to the DOM. When i refresh the page the fetch does not work anymore and i get an error - Uncaught TypeError: Cannot read properties of undefined (reading 'name')
import { useState, useEffect } from "react";
import axios from "axios";
function DataFetching() {
const [products, setProducts] = useState([]);
useEffect(() => {
const loadProducts = async () => {
const response = await axios.get("https://CENSORED/");
setProducts(response.data);
};
loadProducts();
}, []);
return (
<>
<div className="App">
<p>{products.product.name}</p>
</div>
</>
);
}

Thats because for first time render there is no product yet , and after fetching and rerender component it is available , so in first render you have no access to object and it throw error
you can do {products?.product?.name}
Or You can also write your code like
import { useState, useEffect } from "react";
import axios from "axios";
function DataFetching() {
const [products, setProducts] = useState(null);
useEffect(() => {
const loadProducts = async () => {
const response = await axios.get("https://CENSORED/");
setProducts(response.data);
};
loadProducts();
}, []);
if(!products) return <div>loading...</div>
return (
<>
<div className="App">
<p>{products.product.name}</p>
</div>
</>
);
}

It's because the 'products' state have no children yet, so the 'name' is undefined.
A simple solution is to check if the 'products' state have 'product' child inside before rendering.
Like this :
<p>{products.product ? products.product.name : ''}</p>

Related

React hook "useMemo" with array as dependency

I am new to react (that I use with typeScript) and I am facing an issue with the use of the useMemo hook.
Here is my fetching service:
export default class FetchingService {
datas: Data[] = [];
constructor() {
this.fetch();
}
async fetch(): Promise<Data[]> {
const d = // await an async array from an api, using Array.flat()
this.datas = d;
console.log(this.datas);
return d;
}
}
In a component, I try to watch for change of the datas attribute of my service:
import fetchingService from '../services/fetchingService.ts';
const Home: React.FC = () => {
const ds: Data[];
const [datas, setDatas] = useState(ds);
const fetchDatas = useMemo(() => {
console.log('Render datas', fetchingService.datas?.length)
setDatas(fetchingService.datas);
return fetchingService.datas;
}, [fetchingService.datas]);
return (
<ul>{datas.map(d => {
return (
<li key={d.id}>{d.id}</li>
);
</ul>
);
}
The problem I am facing is that the useMemo hook is not recompouted when the datas attribute changes within my fetchService. I am pretty sure that my FetchingService.fetch() function works because the console.log within the fetch function always display the fetched datas.
The observed behavior is that sometimes datas are well rendered (when fetch ends before rendering ...), but sometimes it isn't.
The expected one is that datas are rendered every time and only on refresh, exept when datas are modified
I also tried to put the length of the data array as a dependency in useMemo, but in both cases it doesn't work and I have a warning in my IDE, telling me it is an unnecessary dependency.
I don't really understand if it is a typescript or a specific react behavior issue. I think the reference of the datas attribute should change at the end of the fetch (or at least its length attribute ...), but tell me if I am wrong.
I do appreciate every help !
in fetchingService, when datas change, probably the dependency cannot be accepted. You can use a custom hook in stead of it.
You can use this source about useMemo: useMemo with an array dependency?
import { useState, useLayoutEffect, useCallback } from "react";
export const useFetchingService = () => {
const [fetchedData, setFetchedData] = useState([]);
const fetch = useCallback(async () => {
const d = await new Promise((res, rej) => {
setTimeout(() => {
res([1, 2, 3]);
}, 5000);
}); // await an async array from an api, using Array.flat()
setFetchedData(d);
}, []);
useLayoutEffect(() => {
fetch();
}, []);
return [fetchedData];
};
useLayoutEffect runs before rendering
using:
const [fetchData] = useFetchingService();
const fetchDatas = useMemo(async () => {
console.log("Render datas", fetchData.length);
setDatas(fetchData);
return fetchData;
}, [fetchData]);
You can also use this directly without 'datas' state.
I hope that this will be solution for you.
So I put together a codesandbox project that uses a context to store the value:
App.tsx
import React, { useState, useEffect, createContext } from "react";
import Home from "./Home";
export const DataContext = createContext({});
export default function App(props) {
const [data, setData] = useState([]);
useEffect(() => {
const get = async () => {
const d = await fetch("https://dummyjson.com/products");
const json = await d.json();
const products = json.products;
console.log(data.slice(0, 3));
setData(products);
return products;
};
get();
}, []);
return (
<div>
Some stuff here
<DataContext.Provider value={{ data, setData }}>
<Home />
</DataContext.Provider>
</div>
);
}
Home.tsx
import React, { FC, useMemo, useState, useEffect, useContext } from "react";
import { DataContext } from "./App";
import { Data, ContextDataType } from "./types";
const Home: FC = () => {
const { data, setData }: ContextDataType = useContext(DataContext);
return (
<>
<ul>
{data.map((d) => {
return (
<li key={d.id}>
{d.title}
<img
src={d.images[0]}
width="100"
height="100"
alt={d.description}
/>
</li>
);
})}
</ul>
</>
);
};
export default Home;
This was my first time using both codesandbox and typescript so I apologize for any mistakes

TypeError: X.map is not a function (fetching api into an object not into an array)

new to Reactjs!
I fetch an api (using axios) and then try to map thought it but i cannot because its an object.
How can i fix this! how can i get specific data of that object ?
I wanna fetch the names of the memes.
if i use another API like this one ex." https://www.breakingbadapi.com/api/" and use an extra path ( / ) in html like this
ex. https://www.breakingbadapi.com/api/**characters** i get an array and i get the job done.
Why the same ex. as above doesnt work with my case?
!The Error that i get is memes.map is not a function!
Here is my code:
import React, { useState, useEffect } from "react";
import axios from "axios";
import { Header } from "./components/Header";
import Memes from "./components/Memes";
import "./App.css";
const App = () => {
const [memes, setMemes] = useState({});
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const fetchItems = async () => {
const result = await axios("https://api.imgflip.com/get_memes");
console.log(result.data);
setMemes(result.data);
setIsLoading(false);
};
fetchItems();
}, {});
return (
<div className="App">
<Header />
<Memes isLoading={isLoading} memes={memes} />
</div>
);
};
export default App;
import React from "react";
const Memes = ({ isLoading, memes }) => {
return isLoading ? (
<h1>Loading</h1>
) : (
<section>
{memes.map((meme) => (
<h1 key={meme.char_id}>{meme.name}</h1>
))}
</section>
);
};
export default Memes;
wrong code : const [memes, setMemes] = useState({});
update code :const [memes, setMemes] = useState([]);
On First render memes value will be null ( sync code rendering ). Please try the following
{memes?.map((meme) => (
<h1 key={meme.char_id}>{meme.name}</h1>
))}
? will make sure to run map once the meme array is populated with data.
Also, useState should have [] as init value like:
const [meme, setMeme] = useState([])

I receive "TypeError: items is undefined" when trying to map items from a JSON

I am currently trying to setup a React web app using React hooks. I try to pull the items from the JSON with Map but I receive this error.
TypeError: items is undefined
Shop.js
import React, {useState, useEffect} from 'react';
import './App.css';
function Shop() {
useEffect(() => {
fetchItems();
}, []);
const [items, setItems] = useState([]);
const fetchItems = async () => {
const data = await fetch('https://fortnite-api.theapinetwork.com/upcoming/get');
const items = await data.json();
console.log(items.items);
setItems(items.items);
};
return (
<div>
{items.map(item => (
<h1>{item.map}</h1>
))}
</div>
);
}
export default Shop;
I'm not an expert on React, but I'm pretty sure its because you get to the return statement before you define items.
That function is async, so while it takes its turn running each line, the program itself will move on, thus getting to the return with "items" in it before items is actually defined.
What might fix it is doing an if/else that checks if items is defined, then returns either blank html or the html with items. React dynamically updates so that should then return the correct html once items is loaded.
This should solve your question. I have checked the response from the GET request you provided and you have used the incorrect data structuring when pulling fields out. Try the code below.
import React, {useState, useEffect} from 'react';
import './App.css';
function Shop() {
useEffect(() => {
fetchItems();
}, []);
const [items, setItems] = useState([]);
const fetchItems = async () => {
const response = await fetch('https://fortnite-api.theapinetwork.com/upcoming/get');
const deserialisedResponse = await response.json();
setItems(deserialisedResponse.data);
};
return (
<div>
{items.map((item, index) => (
<h1 key={index}>{item.map}</h1>
))}
</div>
);
}
export default Shop;
I've tried on my end and confirmed it works.
Let me show the codes.
function Shop() {
useEffect(() => {
fetchItems();
}, []);
const [items, setItems] = useState([]);
const fetchItems = async () => {
const response = await fetch('https://fortnite-api.theapinetwork.com/upcoming/get');
const deserialisedResponse = await response.json();
console.log("result: ", deserialisedResponse)
setItems(deserialisedResponse.data);
};
return (
<div>
{items.map((item, idx) => (
<h1 key={idx}>{item.item.name}</h1>
))}
</div>
)
}
Please have a check and let me know if it works or not.

React on page reload receive error TypeError: Cannot read property 'X' of undefined

When I land on a react page that I have created called"feedback," then I see the page display as expected.
However, if I go to refresh the page I receive an error: TypeError: Cannot read property 'email' of undefined
How can I resolve this issue?
import React, {useState, useContext} from "react";
import UserContext from "../context/UserContext";
import axios from 'axios';
const Feedback = () => {
const { userData } = useContext(UserContext);
const [review, setReview] = useState([]);
const filtersActive = () => {
setFilterMode(false);
}
axios
.get(`http://localhost:5000/reviews/reviews?email=${userData.user.email}`, userData)
.then((response) => {
console.log(response)
setReview(response.data)
});
return(
<div className='testing1234'>
{review.map(review => (
<p key={review._id}>{review.review}</p>
))}
</div>
)
}
Only make the request if the property user in userData is defined.
You can add a useEffect which will be executed on userData changes then make the request, like:
useEffect(() => {
if (userData.user) {
axios.get(`http://localhost:5000/reviews/reviews?email=${userData.user.email}`)
}
}, [userData]);

useState not updating for some reason?

when I try to get some data from my backend API using axios, and set the state after I've gotten the result for some reason the state is not updated and when I try to use the state it will only show me an empty array. but what's so interesting is that when I console.log(res.data) it will show me my array of lists with no problem, so I guess the problem is with the setCategories() state function. What am I doing wrong?
const Home = (props) => {
const [categories, setCategories] = useState([]);
useEffect(() => {
getCats();
}, []);
const getCats = async () => {
const data = await axios.get(`${myUrl}/allItems`, {
withCredentials: true,
});
const cats = await data.data;
console.log(cats); //this one works perfectly
setCategories(cats);
console.log(categories) //this one doesn'nt work which means the setState didn't work
};
return (
<>
<div className="card-div mt-5">
{categories.map((cat) => {
<li>{cat.name}</li>;
})}
</div>
</>
);
};
the state is set asynchronously, so the data is not updated instantly. that's why you are not getting the output on console.log(categories) right after setCategories(cats);
here is a small example of asynchronous behaviour of useState state update:
Link to working example: stackblitz
import React, { useEffect, useState } from "react";
import "./style.css";
import axios from "axios";
const url = "https://jsonplaceholder.typicode.com/users";
export default function App() {
const [users, setUsers] = useState([]);
useEffect(() => {
axios.get(url).then(result => {
console.log("1. when data is fetched sucessfully: ", result.data);
setUsers(result.data);
console.log("2. Just after setting state: ", users);
});
}, []);
// secons useEffect for logging out upadated todos useState
useEffect(() => {
console.log("todos upadated: ", users);
}, [users]);
return (
<div>
<h1>Hello StackBlitz!</h1>
<p>Start editing to see some magic happen :)</p>
{users.map(user => (
<p>{user.name}</p>
))}
</div>
);
}
Here is what is happening in the above example:
You can see the flow of data fetching and async update of state.
The useState function is asynchronous, so you will never get the new state in the same function, the best way is to use it in another function or useEffect.
Example:
useEffect(() => {
console.log(categories);
}, [categories]);

Resources