How to update an array using useState Hook - javascript

I've tried to fetch data from a URL and get the result as JSON format, then store not of the object result in my state. but it always returns an empty array.
const [genres, setGenres] = useState([]);
useEffect(() => {
const getGenres = async () => {
fetch("https://quote-garden.herokuapp.com/api/v2/genres")
.then((response) => response.json())
.then((data) => {
for (const g of data.genres) {
setGenres((oldGenres) => [...oldGenres, g]);
}
});
};
getGenres();
}, []);
Here is the code:
I don't see where the problem can be.
ps: I deleted the import so the code is more readable
import React, { useEffect, useState } from "react";
function App() {
const [quoteOfTheDay, setQuoteOfTheDay] = useState("");
const [authorOfQod, setAuthorOfQod] = useState("");
useEffect(() => {
const getQuoteOfTheDay = async () => {
fetch("https://quotes.rest/qod?language=en")
.then((response) => response.json())
.then((data) => {
const qod = data.contents.quotes[0].quote;
const author = data.contents.quotes[0].author;
setQuoteOfTheDay(qod);
setAuthorOfQod(author);
});
};
getQuoteOfTheDay();
}, []);
const [genres, setGenres] = useState([]);
useEffect(() => {
const getGenres = async () => {
fetch("https://quote-garden.herokuapp.com/api/v2/genres")
.then((response) => response.json())
.then((data) => {
for (const g of data.genres) {
setGenres((oldGenres) => [...oldGenres, g]);
}
});
console.log(genres); // genres always empty
};
getGenres();
}, []);
return (
<div className="app">
<Head quoteOfTheDay={quoteOfTheDay} author={authorOfQod} />
<div className="app__category">
<QuoteCategory genre="sport" />
</div>
</div>
);
}
export default App;
Thank you so much

I think it should work if you change
for (const g of data.genres) {
setGenres((oldGenres) => [...oldGenres, g]);
}
to
setGenres((oldGenres) => [...oldGenres, ...data.genres]);

Are you sure that
useEffect(() => {
const getGenres = async () => {
fetch("https://quote-garden.herokuapp.com/api/v2/genres")
.then((response) => response.json())
.then((data) => {
setGenres(data.genres);
});
};
getGenres();
}, []);
is not enough? :)
Up. If you began you can use async-await syntax till the end. It looks more neatly.
useEffect(() => {
const getGenres = async () => {
const response = await fetch("https://quote-garden.herokuapp.com/api/v2/genres");
const { genres } = await response.json();
setGenres(genres);
};
getGenres();
}, []);

you should put genresState as your dependency
const [genresState, setGenres] = useState([])
useEffect(() => {
const getGenres = async () => {
const response = await fetch("https://quote-garden.herokuapp.com/api/v2/genres");
const { genres } = await response.json();
setGenres(genres);
};
getGenres();
}, [genresState]);

Related

Why .then() which is in useEffect is not updating my state

targetMovie is null when it comes to rednering. I couldn't find any solutions. First time having unsolvable problem. Please help!
async function getMovie(id) {
try {
const res = await axios.get(apiEndPoint + "/" + id);
const movies = await res.data;
return movies;
} catch (err) {
console.log(err);
}
}
const MovieEdit = () => {
const { id } = useParams();
const [targetMovie, setTargetMovie] = useState(null);
useEffect(() => {
getMovie(id)
.then((mov) => {
setTargetMovie(mov);
console.log(mov);
})
.catch((err) => console.log(err));
}, []);
console.log(targetMovie);
if (targetMovie) return <AddMovie movie={targetMovie} />;
return <Navigate to="/not-found" />;
};
You need to represent 3 states:
You're currently waiting on getMovie to complete
getMovie completed successfully
getMovie completed and returned null/undefined
You're currently using the same condition (!targetMovie) to represent both 1. and 3. which is why you're running into issues.
Try this:
const MovieEdit = () => {
const { id } = useParams();
const [isFetching, setIsFetching] = useState(true);
const [targetMovie, setTargetMovie] = useState(null);
useEffect(() => {
getMovie(id)
.then((mov) => {
setIsFetching(false);
setTargetMovie(mov);
console.log(mov);
})
.catch((err) => {
console.log(err));
setIsFetching(false);
}
}, []);
if (isFetching) return null;
if (targetMovie) return <AddMovie movie={targetMovie} />;
return <Navigate to="/not-found" />;
};

display async api request on react js

i have this code that shows the data on console but how can i display the data of all 3 apis at the same time on the page using react app .JSX using .map ?
(async () => {
try {
const urls = [
"https://api.chucknorris.io/jokes/random",
"https://api.chucknorris.io/jokes/random",
"https://api.chucknorris.io/jokes/random",
"https://api.chucknorris.io/jokes/random",
];
const requests = urls.map((url) => fetch(url));
const responses = await Promise.all(requests);
const errors = responses.filter((response) => !response.ok);
if (errors.length > 0) {
throw errors.map((response) => Error(response.statusText));
}
const json = responses.map((response) => response.json());
const data = await Promise.all(json);
data.forEach((datum) => console.log(datum));
}
catch (errors) {
errors.forEach((error) => console.error(error));
}
})();
You can use Promise.All method and save the resolved data to a state and map accordingly to render data ...
A sample e.g. below
const { useState, useEffect } = React;
const urls = [
"https://api.chucknorris.io/jokes/random",
"https://api.chucknorris.io/jokes/random",
"https://api.chucknorris.io/jokes/random",
"https://api.chucknorris.io/jokes/random",
];
const App = () => {
const [data, setData] = useState();
useEffect(() => {
const promises = urls.map((url) =>
fetch(url)
.then((res) => res.json())
.then((data) => data)
);
Promise.all(promises).then((values) => {
setData(values);
});
}, []);
if (!data) return "Loading ..."; // this is not exact way to do a loading state just for illustration ...
return (
<div>
{data.map((item) => (
<p key={item.id}>{item.value}</p>
))}
</div>
);
};
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

React axios await dispatch in UseEffect

I need to wait for my axios action to dispatch before continuing in UseEffect
import { getBrief } from '../../store/actions/agency-brief'
import agencyBriefReducer from '../../store/reducers/agency-brief-reducer'
const [agencyBrief, dispatch] = useReducer(agencyBriefReducer, [])
useEffect(async () => {
await getBrief(briefId)(dispatch);
console.log('BRIEF', agencyBrief)
}, [])
So console log here is undefined, I need to wait before
action:
export const getBrief = (briefID) => async (dispatch) => {
try {
let res = await axiosInstance.get(`/agency_briefs/reactGetBrief/${briefID}`)
console.log('sre', res )
return dispatch({
type: 'GET_BRIEF',
payload: res.data
})
}
catch (err) {
console.error(err);
}
}
reducer:
const agencyBriefReducer = (state, {payload, type}) => {
switch (type) {
case 'GET_BRIEF':
return {
...state,
brief: payload
}
default:
return state
}
}
I've tried async / await but it doesn't work, I need to wait so I can use the result to set the state ? Thanks
ok, so one of the comments suggested 2 useEffects and that does the trick:
useEffect(() => {
getBrief(briefId)(dispatch);
}, [])
useEffect(() => {
console.log('BRIEF', agencyBrief)
}, [agencyBrief.brief])
const [agencyBrief, dispatch] = useReducer(agencyBriefReducer, [])
useEffect(async () => {
await getBrief(briefId)(dispatch);
console.log('BRIEF', agencyBrief) // will always console []
}, [])
Start by explaining why the source code does not behave as expected. useEffect(() => {}, []) Since the dependency is an empty array, this method will only be executed once when the page is initialized. In this render, the value of agencyBrief is always [], will not change.
If you want to monitor the change of the result after the await asynchronous request ends, you can do this:
const [agencyBrief, dispatch] = useReducer(agencyBriefReducer, [])
useEffect(async () => {
await getBrief(briefId)(dispatch);
}, [])
useEffect(() => {
// Now agencyBrief is the latest value after await async request ends
console.log('BRIEF', agencyBrief)
}, [agencyBrief])
You can achieve it, I hope that would help.
import { getBrief } from '../../store/actions/agency-brief'
import agencyBriefReducer from '../../store/reducers/agency-brief-reducer'
const [agencyBrief, dispatch] = useReducer(agencyBriefReducer, []);
const getBrief = async () => {
await getBrief(briefId)(dispatch);
}
useEffect(() => {
getBrief();
}, [])
useEffect(() => {
if (agencyBrief) {
// perform action
}
}, [agencyBrief]);
This should resolve your issue .
import { getBrief } from '../../store/actions/agency-brief'
import agencyBriefReducer from '../../store/reducers/agency-brief-reducer'
const [agencyBrief, dispatch] = useReducer(agencyBriefReducer, [])
useEffect(async () => {
await getBrief(briefId)(dispatch);
setTimeOut(()=>console.log('BRIEF', agencyBrief),10);
}, [])

How to update lists after delete a category

I want to update the Category list after delete a category, I used custom hook for fetching data from the server. I'm not sure how to update state on custom fetch hook
const {data, error, loading} = useFetch("/api/admin/category");
const [category, setCategory]= useState([]);
useEffect(() => {
setCategory(data)
},[])
const deleteHandler = (id) => {
const deleteRequest = async () => {
const data = await axios.delete(`/api/admin/category/${id}`);
return data;
}
deleteRequest()
.then(res => {
data.filter((item) => {
return id !== item.id;
})
})
}
Adding data as the dependency to the useEffect hook may help, try this,
const {data, error, loading} = useFetch("/api/admin/category");
const [category, setCategory]= useState([]);
useEffect(() => {
setCategory(data)
},[data])
const deleteHandler = (id) => {
const deleteRequest = async () => {
const data = await axios.delete(`/api/admin/category/${id}`);
return data;
}
deleteRequest()
.then(res => {
data.filter((item) => {
return id !== item.id;
})
})
}
could you add you're custom hook to the code provided? and also be a bit more specific with your query?
import {useEffect, useState} from "react";
import axios from "axios";
const useFetch = (url) => {
const [data, setData,isLoading] = useState([]);
const [error, setError] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
request_get()
.then(res => {
if (res.request.status === 200) {
setTimeout(() => {
setLoading(false)
setData(res.data.data)
},1000)
}
})
return () => {
setData([]);
}
}, [])
const request_get = async () => {
const data = await axios.get(url)
return data;
}
return {data: data, error: error, loading:loading}
}
export default useFetch;
in your custom hook add data to your dependency array;
in your component instead of the folowing
useEffect(() => {
setCategory(data)
},[])
try to use the spread operator
useEffect(() => {
setCategory([...data])
},[data])

React .map not rendering

I've done a API call to get some datas then store it in an array and do a .map in the return
This is the code if you guys have any ideas it's been 2 hours that i'm stuck on this :(
import {useEffect, useState} from 'react';
import {useParams} from "react-router-dom";
import axios from "axios";
const CharacterScreen = () => {
const params = useParams()
const [character, setCharacter] = useState([]);
const [starships, setStarships] = useState([]);
useEffect(() => {
axios.get(`https://swapi.dev/api/people/?search=${params.character}`)
.then((r) => {
setCharacter(r.data.results[0])
getStarships(r.data.results[0])
})
.catch((e) => console.log(e))
const getStarships = (data) => {
let array = []
data.starships.forEach(element => {
axios.get(element)
.then((r) => {
array.push(r.data)
})
.catch((e) => console.log(e))
})
console.log(array)
setStarships(array)
}
}, []);
console.log(starships)
return (
<div>
<p>{character.name}</p>
<p>{character.eye_color}</p>
<p>{character.birth_year}</p>
<p>{character.gender}</p>
<p>{character.created}</p>
<p>{character.edited}</p>
{starships.map((element) => {
console.log('ok')
return (
<p key={element.key}>{element.name}</p>
)
})}
</div>
)
}
This is the .log of starships :
This is my return :
Any help would be apréciated
Use spread operator :
useEffect(() => {
axios.get(`https://swapi.dev/api/people/?search=${params.character}`)
.then((r) => {
setCharacter(r.data.results[0])
getStarships(r.data.results[0])
})
.catch((e) => console.log(e))
const getStarships = (data) => {
let array = []
data.starships.forEach(element => {
axios.get(element)
.then((r) => {
array.push(r.data)
})
.catch((e) => console.log(e))
})
setStarships([...array]) <=== //spread opeator
}
}, []);
The code inside your forEach will run asynchronously. You would have to wait for all that data to be actually populated in your array. async/await pattern + Promise.all(..) would be a good bet here and can be done like so :-
const getStarships = async (data) => {
let array = await Promise.all(data.starships.map(element => {
return axios.get(element)
.then((r) => {
return r.data
})
.catch((e) => console.log(e));
}))
setStarships(array);
}
Currently in your code by the time you do setStarships(array), array will be empty i.e. [].
Check this codesandbox where it's working :-
Note :- Don't pay attention to element.replace code, thats just for making secure requests
You have a syntax error, you should replace your bracket with a parenthesis like following:
{starship && starships.map((element) => (//here
console.log('ok')
return (
<p key={element.key}>{element.name}</p>
)
)//and here)}

Categories

Resources