Conditional API Fetching using ReactApp Javascript - reactjs

I need some help with my code where it has to display the API data when the condition is the following
use componentWillReceiveProps()
when count == 2 it will fetch api
import React, {useState, useEffect,Component} from "react";
var x = 2;
const App =() => {
const [hasError, setErrors]= useState(false)
const [apps, setApps] = useState ("Loading");
useEffect(()=>{
async function fetchData(){
const res = await fetch ("https://m2dzass19b.execute-api.ap-southeast-1.amazonaws.com/dev/api/vdrOptimize/21-09-2019");
res
.json()
.then(res => setApps(res))
.catch(err => setErrors(err))
}
fetchData();
} );
return(
<div>
<span>{JSON.stringify(apps)}</span>
<hr/>
</div>
);
}

Okay here's the solution
Change your useEffect as this
useEffect(() => {
async function fetchData() {
const res = await fetch(
"https://m2dzass19b.execute-api.ap-southeast-1.amazonaws.com/dev/api/vdrOptimize/21-09-2019"
);
res
.json()
.then(res => setApps(res))
.catch(err => setErrors(err));
}
if (props.id === 2) {
fetchData();
}
}, [props.id]);
Hope it helps
UPDATE: This is how you can do it using comonentWillReceiveProps
UNSAFE_componentWillReceiveProps(nextProps) {
async function fetchData() {
const res = await fetch(
"https://m2dzass19b.execute-api.ap-southeast-1.amazonaws.com/dev/api/vdrOptimize/21-09-2019"
);
res
.json()
.then(res => this.setState({ apps: res }))
.catch(err => this.setState({ hasError: err }));
}
if (nextProps.id === 2) {
fetchData.call(this);
}
}
Sandbox
Note: componentWillReceiveProps() is deprecated lifecycle method

Related

React JS + Axios return undefined first

I trying make an axios get from context file into function and call this from component to return data.
Context file:
const getPets = async () => {
await axios.get('http://localhost:1337/api/pets?populate=*')
.then((res) => {
return res.data
})
.catch(err => {
console.log(err)
})}
Component file:
const [pets, setPets] = useState([])
useEffect( () => {
setPets(getPets())},[])
return (console.log(pets))
The return value is undefined and i don't know why.
Can we help me please?
Tks!
Modify getPets():
const getPets = async () => {
const res = await axios.get('http://localhost:1337/api/pets? populate=*');
return res.data;
}
getPets() returns a promise
useEffect(() => {
getPets().then(res => setPets(res));
}, []);
return (
<>
{pets?.map(pet => { /* some JSX */})}
</>
);

React update state when click

import React, { useEffect, useState } from "react";
import { Container, Row } from "react-bootstrap";
import ListCategories from "./ListCategories";
import Hasil from "./Hasil";
import DaftarProduk from "./DaftarProduk";
import axios from "axios";
import keranjang from "../utils/keranjang";
import BASE_URL from "../utils/constata";
const Main = () => {
const [dataProduct, setDataProduct] = useState([]);
const [dataCategories, setDataCategories] = useState([]);
const [categoriesId, setCategoriesId] = useState(1);
const [listPesanan, setListPesanan] = useState([]);
const handleListCategories = (id) => {
setCategoriesId(id);
};
const handleProdukClick = async (produk) => {
keranjang(produk);
};
useEffect(() => {
const getProducts = async () => {
let responseJson = [];
try {
responseJson = await axios.get(
BASE_URL + "products?category.id=" + categoriesId
);
} catch (error) {
console.log("Ada yang " + error);
} finally {
setDataProduct(responseJson.data);
}
};
const getCategories = async () => {
let responseJson = [];
try {
responseJson = await axios.get(BASE_URL + "categories");
} catch (error) {
console.log("Ada yang " + error);
} finally {
setDataCategories(responseJson.data);
}
};
const getPesanans = async () => {
let responseJson = [];
try {
responseJson = await axios.get(BASE_URL + "keranjangs");
} catch (error) {
console.log("Ada yang " + error);
} finally {
setListPesanan(responseJson.data);
}
};
getProducts();
getCategories();
getPesanans();
}, [categoriesId]);
return (
<Container className="mt-3">
<Row>
{dataCategories && (
<ListCategories
categories={dataCategories}
handleClick={handleListCategories}
categoriesActive={categoriesId}
></ListCategories>
)}
{dataProduct && (
<DaftarProduk
produk={dataProduct}
handleClick={handleProdukClick}
></DaftarProduk>
)}
<Hasil pesanan={listPesanan}></Hasil>
</Row>
</Container>
);
};
export default Main;
How to update listPesanan, when handleProdukClick was click?
if me put listPesanan inside
useEffect(() => {
const getProducts = async () => {
let responseJson = [];
try {
responseJson = await axios.get(
BASE_URL + "products?category.id=" + categoriesId
);
} catch (error) {
console.log("Ada yang " + error);
} finally {
setDataProduct(responseJson.data);
}
};
const getCategories = async () => {
let responseJson = [];
try {
responseJson = await axios.get(BASE_URL + "categories");
} catch (error) {
console.log("Ada yang " + error);
} finally {
setDataCategories(responseJson.data);
}
};
const getPesanans = async () => {
let responseJson = [];
try {
responseJson = await axios.get(BASE_URL + "keranjangs");
} catch (error) {
console.log("Ada yang " + error);
} finally {
setListPesanan(responseJson.data);
}
};
getProducts();
getCategories();
getPesanans();
}, [categoriesId, listPesanan]);
that's causes looping to send request to server
my full code in here
Yes, your second 'solution' will cause looping, because you have listPesanan in you dependency array (The second parameter to the useEffect function), so whenever listPesanan changes, the useEffect is run again. And in fact, you are updating the value of listPesanan in the useEffect, which causes the useEffect to get triggered again, which causes you to update the value of listPesanan, and so on.
If I understand your question/code, a simple solution would just be to declare the getPesanans function outside of the useEffect hook, and then have your DaftarProduk onClick call that function. Then, remove listPesanan from the dependency array of useEffect. Your code would look like this:
import React, { useEffect, useState } from "react";
import { Container, Row } from "react-bootstrap";
import ListCategories from "./ListCategories";
import Hasil from "./Hasil";
import DaftarProduk from "./DaftarProduk";
import axios from "axios";
import keranjang from "../utils/keranjang";
import BASE_URL from "../utils/constata";
const Main = () => {
const [dataProduct, setDataProduct] = useState([]);
const [dataCategories, setDataCategories] = useState([]);
const [categoriesId, setCategoriesId] = useState(1);
const [listPesanan, setListPesanan] = useState([]);
**const getPesanans = async () => {
let responseJson = [];
try {
responseJson = await axios.get(BASE_URL + "keranjangs");
} catch (error) {
console.log("Ada yang " + error);
} finally {
setListPesanan(responseJson.data);
}
};**
const handleListCategories = (id) => {
setCategoriesId(id);
};
const handleProdukClick = async (produk) => {
keranjang(produk);
**await getPesanans();**
};
useEffect(() => {
const getProducts = async () => {
let responseJson = [];
try {
responseJson = await axios.get(
BASE_URL + "products?category.id=" + categoriesId
);
} catch (error) {
console.log("Ada yang " + error);
} finally {
setDataProduct(responseJson.data);
}
};
const getCategories = async () => {
let responseJson = [];
try {
responseJson = await axios.get(BASE_URL + "categories");
} catch (error) {
console.log("Ada yang " + error);
} finally {
setDataCategories(responseJson.data);
}
};
getProducts();
getCategories();
getPesanans();
}, [categoriesId]);
return (
<Container className="mt-3">
<Row>
{dataCategories && (
<ListCategories
categories={dataCategories}
handleClick={handleListCategories}
categoriesActive={categoriesId}
></ListCategories>
)}
{dataProduct && (
<DaftarProduk
produk={dataProduct}
handleClick={handleProdukClick}
></DaftarProduk>
)}
<Hasil pesanan={listPesanan}></Hasil>
</Row>
</Container>
);
};
export default Main;
problem
First handleProdukClick triggers another effect, keranjang.
const handleProdukClick = async (produk) => { // ⚠️ async misuse
keranjang(produk); // ⚠️ effect
// ✏️ update listPesanan here ...
};
code review
Your linked source code shows this implementation for keranjang. This code misunderstands how to effectively apply async and await. Let's fix that first.
⚠️ await..then anti-pattern doesn't assign result to variable
⚠️ 38-LOC function with mixed concerns
⚠️ Errors are swallowed so caller cannot respond to them
const keranjang = async (produk) => {
let pesanan = {
jumlah: 1,
total_harga: produk.harga,
product: produk,
};
const popUpPesanan = () => // ⚠️ nested function, no arguments
swal({
title: "Berhasil",
text: "Masuk Keranjang " + produk.nama,
icon: "success",
button: "Oke",
});
await axios // ⚠️ no return
.get(BASE_URL + "keranjangs?product.id=" + produk.id)
.then(async (res) => {
if (res.data.length === 0) {
await axios // ⚠️ no return
.post(BASE_URL + "keranjangs", pesanan)
.then(() => popUpPesanan())
.catch((error) => console.log(error)); // ⚠️ swallow error
} else {
pesanan = { // ⚠️ variable reassignment
jumlah: res.data[0].jumlah + 1,
total_harga: res.data[0].total_harga + produk.harga,
product: produk,
};
await axios // ⚠️ no return
.put(BASE_URL + "keranjangs/" + res.data[0].id, pesanan)
.then(() => popUpPesanan())
.catch((error) => console.log(error)); // ⚠️ swallow error
}
})
.catch((error) => console.log(error)); // ⚠️ swallow error
};
First write keranjang from a high-level.
✅ Reusable get, add, and increase functions decouple separate concerns
✅ Return Promise so handleProduckClick can know when it is complete
✅ Pass arguments to functions instead of reassigning variables
✅ Do not swallow error messages with .catch
const keranjang = async (produk) => {
const pesanan = await get(produk.id)
if (pesanan.length === 0)
return add(produk).then(() => popUpPesanan(produk))
else
return increase(pesanan[0], produk).then(() => popUpPesanan(produk))
}
✅ Implement get, add, increase
✅ Each function returns a Promise
const get = (id) =>
axios.get(`${BASE_URL}keranjangs?product.id=${id}`).then(res => res.data)
const add = (produk) =>
axios.post(`${BASE_URL}keranjangs`, {
jumlah: 1,
total_harga: produk.harga,
product: produk,
})
const increase = (pesanan, produk) =>
axios.put(`${BASE_URL}keranjangs/${pesanan.id}`, {
jumlah: pesanan.jumlah + 1,
total_harga: pesanan.total_harga + produk.harga,
product: produk,
})
popUpPesanan accepts a produk argument
const popUpPesanan = (produk) =>
swal({
title: "Berhasil",
text: "Masuk Keranjang " + produk.nama,
icon: "success",
button: "Oke",
})
fix #1
With all of that fixed, handleProdukClick can appropriately respond to the keranjang call.
const handleProdukClick = async (produk) => {
try {
await keranjang(produk) // ✅ await
setListPesanan(await getPesanans())// ✅ await and set
} catch (err) {
console.log(err) // ✅ caller handles error
}
};
You don't need async and await for this. It's easier to write
const handleProdukClick = (produk) => {
keranjang(produk).then(setListPesanan).catch(console.error)
}
fix #2
Now you have to move getPesanans out of the useEffect call in your component and decouple the mixed concerns like you did with keranjang...
import { getProducts, getCategories, getPesanans } from "./api"
const Main = () => {
const [dataProduct, setDataProduct] = useState([])
const [dataCategories, setDataCategories] = useState([])
const [categoriesId, setCategoriesId] = useState(1)
const [listPesanan, setListPesanan] = useState([])
const handleListCategories = (id) => {
setCategoriesId(id)
}
const handleProdukClick = (produk) => {
keranjang(produk).then(setListPesanan).catch(console.error)
}
useEffect(() => {
getProducts(categoriesId).then(setDataProduct).catch(console.error)
getCategories().then(setDataCategories).catch(console.error)
getPesanans().then(setListPesanan).catch(console.error)
}, [categoriesId]) // ✅ no infinite loop
return (...)
}
api
Define a reusable api module so each component does not need to concern itself with axios, building URLs, or picking apart responses.
✅ Reusable functions separate concerns
✅ .then(res => res.data) allows caller to access data directly
✅ Errors are not swallowed
✅ axios.create makes instance so you don't have to add BASE_URL to everything
import axios from "axios"
import BASE_URL from "../utils/constata";
const client = axios.create({ baseURL: BASE_URL })
const getProducts = (categoriesId) =>
client.get("/products?category.id=" + categoriesId).then(res => res.data)
const getCategories = () =>
client.get("/categories").then(res => res.data)
const getPesanans = () =>
client.get("/keranjangs").then(res => res.data)
export { getProducts, getCategories, getPesanans }
homework
✏️ Move get, add, and increase functions you wrote in keranjang to the api module.
✏️ Remove unnecessary BASE_URL
✏️ Rename them accordingly and add them to the export list
✏️ Any time you see axios spilling into your other components, refactor and move them to your api module
✏️ Consider using transformRequest and transformResponse in your axios config so you don't have to add .then(res => res.data) to each request
✏️ Consider decoupling swal and keranjang. keranjang can move to the api module and swal can be called from your component.
// api.js
const keranjang = async (produk) => {
const pesanan = await get(produk.id)
if (pesanan.length === 0)
return add(produk)
else
return increase(pesanan[0], produk)
}
// component.js
const Main = () => {
// ...
const handleProdukClick = (produk) => {
keranjang(produk)
.then(setListPesanan)
.then(_ => swal({
title: "Berhasil",
text: "Masuk Keranjang " + produk.nama,
icon: "success",
button: "Oke",
}))
.catch(console.error)
}
// ...
}
✏️ util/keranjang is now an empty module and can be removed

Refactoring to Fetch API only once React.JS

I am building a Project with the Pokemon API. Here it is how I am fetching the data:
pokeAPI.js
export const api = {
getPokemonList: async ({ url }) => {
return new Promise((resolve) => {
fetch(url)
.then(res => res.json())
.then(data => {
resolve(data)
})
});
},
getPokemonInfo: async (url) => {
return new Promise((resolve) => {
fetch(url)
.then(res => res.json())
.then(data => {
resolve(data)
})
});
}
};
App.js
const [pokemon, setPokemon] = useState([]);
const URL = 'https://pokeapi.co/api/v2/pokemon?limit=150';
useEffect(() => {
const getPokemonInfo = async () => {
const json = await api.getPokemonInfo(URL);
await loadPokemon(json.results);
};
getPokemonInfo();
}, []);
const loadPokemon = async (data) => {
let pokemonData = await Promise.all(data.map(async pokemon => {
let pokemonList = await api.getPokemonList(pokemon)
return pokemonList
}))
setPokemon(pokemonData);
};
Although this works, it's currently calling getPokemonList for every pokemon and the fact that there are multiple async / await is not helping with readiability. How could I refactor this logic:
const loadPokemon = async (data) => {
let pokemonData = await Promise.all(data.map(async pokemon => {
let pokemonList = await api.getPokemonList(pokemon)
return pokemonList
}))
setPokemon(pokemonData);
};
to fetch only once, using a memoized value in a hook to prevent multiple re-renders?
Thanks for helping.
`there are several ways to do it, you can use redux like state managements or browsers local storage
const App = () => {
const [pokemons, setPokemons] = useState([]);
useEffect(() => {
let pokemons= localStorage.getItem("users");
if (pokemons) {
pokemons = JSON.parse(pokemons);
setPokemons({ pokemons });
} else {
fetch("https://pokeapi.co/api/v2/pokemon?limit=150")
.then(res => res.json())
.then(pokemons => {
setPokemons({ pokemons });
localStorage.setItem("pokemons", JSON.stringify(pokemons));
});
}
}, [])
}

How can I use response data from an API to call another different API request in React UseEffect?

I was wondering how can I call an API that requires data from other API. In my case I want to get the coordinates; lattitude and longitute and make another API call with these to retrieve the information I need.
Here is App.js file
import React from 'react';
import './App.css';
import CityInput from "./components/CityInput";
import {Container} from 'react-bootstrap';
import UseFetch from "./hooks/useFetch";
import {API_BASE_URL, API_KEY} from "./apis/config";
import WeatherList from "./components/WeatherList";
const App = () => {
const {data, error, isLoading, setUrl} = UseFetch();
const getContent = () => {
if(error) return <h2>Error when fetching: {error}</h2>
if(!data && isLoading) return <h2>LOADING...</h2>
if(!data) return null;
return <WeatherList weathers={data.list}/>
};
return (
<Container className="App">
{/* user types a city and clicks search*/}
<CityInput onSearch={(city) => setUrl(`${API_BASE_URL}/data/2.5/forecast?q=${city}&appid=${API_KEY}&units=metric`)} />
{getContent()}
</Container>
);
}
export default App;
and here is my UseFetch.js file
import {useState, useEffect} from 'react';
import {API_BASE_URL, API_KEY} from "../apis/config";
const UseFetch = (initialUrl) => {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(null);
const [url, setUrl] = useState(initialUrl);
useEffect(() => {
if(!url) return;
setIsLoading(true);
setData(null);
setError(null);
fetch(url)
.then((response) => response.json())
.then((data) => {
setIsLoading(false);
if(data.cod >= 400) {
setError(data.message);
return;
}
setData(data);
console.log(data);
console.log(data.city.coord.lat);
console.log(data.city.coord.lon);
})
.catch((error) => {
setIsLoading(false);
setError(error);
});
//
// console.log('HIIIIII'+ data);
// fetch(`${API_BASE_URL}/data/2.5/onecall?lat=${data.city.coord.lat}&lon=${data.city.coord.lon}&exclude=minutely&appid=${API_KEY}`)
// .then((response) => response.json())
// .then((data2) => {
// setIsLoading2(false);
// setData2(data2);
// console.log(data2);
// })
// .catch((error2) => {
// setIsLoading2(false);
// setError2(error);
// });
}, [url]);
return { data, error, isLoading, setUrl};
};
export default UseFetch;
I want to retrieve lantitude and lontitude so i can make another fetch
fetch(`${API_BASE_URL}/data/2.5/onecall?lat=${data.city.coord.lat}&lon=${data.city.coord.lon}&exclude=minutely&appid=${API_KEY}`)
But this doesnt seem to work.
I'm using these API for reference:
https://openweathermap.org/forecast5
https://openweathermap.org/api/one-call-api
Call it right after the Initial Api sends back the response for example:
fetch(APIURL)
.then(response => {
/** do any operations using received response data **/
/** Calling second api **/
fetch(API_URL_ + response.data.url)
.then(data => {
setData(data)
})
.catch(error)
}).catch(error)
UseFetch.js
import {useState, useEffect} from 'react';
import {API_BASE_URL, API_KEY} from "../apis/config";
const UseFetch = (initialUrl) => {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(null);
const [url, setUrl] = useState(initialUrl);
useEffect(() => {
if(!url) return;
setIsLoading(true);
setData(null);
setError(null);
fetch(url)
.then((response) => response.json())
.then((data) => {
setIsLoading(false);
if(data.cod >= 400) {
setError(data.message);
return;
}
setData(data);
console.log(data);
console.log(data.city.coord.lat);
console.log(data.city.coord.lon);
console.log('HIIIIII'+ data);
fetch(`${API_BASE_URL}/data/2.5/onecall?lat=${data.city.coord.lat}&lon=${data.city.coord.lon}&exclude=minutely&appid=${API_KEY}`)
.then((response) => response.json())
.then((data2) => {
setIsLoading2(false);
setData2(data2);
console.log(data2);
})
.catch((error2) => {
setIsLoading2(false);
setError2(error);
});
})
.catch((error) => {
setIsLoading(false);
setError(error);
});
}, [url]);
return { data, error, isLoading, setUrl};
};
export default UseFetch;
You can try to chain another .then() after save the first response in a variable??Something like:
let anyVariable;
fetch(url)
.then((response) => response.json())
.then((data) => {
setIsLoading(false);
if(data.cod >= 400) {
setError(data.message);
return;
}
anyVariable = data.city.coord
})
.then(() => {
fetch(`${API_BASE_URL}/data/2.5/onecall?lat=${anyVariable.lat}&lon=${anyVariable.lon}&exclude=minutely&appid=${API_KEY}`)
.then((response) => response.json())
.then((data2) => {
setIsLoading2(false);
setData2(data2);
console.log(data2);
})
})
Any way i think it will be cleaner and better performing to use axios and async await. Also notice that useState is asynchronous.

React useEffect gets data from database but not in time to be used in the component

I am using useEffect to get data from an api.
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`/api/posts/getCats`
);
const cats = await response.json();
console.log(cats);
} catch (e) {
console.error(e);
}
};
fetchData();
}, []);
The problem is when I try to use it in the return, its value is undefined.
{cats.map((data) => {
cats has value when I console.log it.
I cannot use componentDidMount because all my code is functional components.
Edit: I updated the code as per answers below but still get
TypeError: cats.map is not a function
All answers below actually make sense but I am not sure why its not working.
export default function Posts() {
const [cats, setCats] = useState();
useEffect(() => {
fetch(`/api/posts/getCats`)
.then(res => res.json())
.then(setCats)
.catch(console.error);
}, []);
return (
<div>
{cats?.map((data) => {
<h4>{data.main}</h4>
})}
</div>
)
}
This is because React renders your screen before finishing to get response from API. When you render screen, variable cats doesn't have values. You can run useEffect after each rendering. You can rerender by changing state from useEffect (This technique is often used). Do not forget to add [] or [cats] as a dependency of useEffect (second params) otherwise you will get infinite loop.
Below code works even when cats === [] or some array.
export default () => {
const [cats, setCats] = useState([])
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`/api/posts/getCats`
);
const result = await response.json();
setCats(result)
} catch (e) {
}
};
fetchData();
}, []);
return (
<div>
{cats.map(cat => <div>cat</div>)}
</div>)
}
You have to map the cats data into state.
const [cats, setCats] = useState([]);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`/api/posts/getCats`
);
const data = await response.json();
setCats(data);
} catch (e) {
console.error(e);
}
};
fetchData();
}, []);
You need to
call setCats when the response comes back (right now, you're just logging it)
.map only once cats has been populated:
const [cats, setCats] = useState();
useEffect(() => {
fetch(`/api/posts/getCats`)
.then(res => res.json())
.then(result => setCats(result.cats))
.catch(console.error);
}, []);
return (
<div>
{cats?.map((data) => {
// ...

Resources