Reputation: 1222
I'm struggling with the concept of Promise
in JavaScript. I'm writing a React app that makes a GET
call out to a separate API service in Java, and I want to store its state in a useState()
hook. So here's my fetch
code:
const ratingsUrl = "%URL%";
const base64 = require("base-64");
const login = "login";
const password = "password";
function fetchRatings() {
return fetch(ratingsUrl, {
method: "GET",
headers: new Headers({
Authorization: "Basic " + base64.encode(login + ":" + password),
}),
})
.then((response) => response.json())
.catch(handleError);
}
And now I'm trying to store its state in a hook in my page component:
function DisplayPage(){
const [ratings, setRatings] = useState(fetchRatings());
.
.
.
}
Now, the data returns but it's in a Promise
, hence causing errors down the line:
Promise {<pending>}
__proto__: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: Array(20)
What I need to do is to initialise the data in a hook and return it in a Table
so I can map through it. However, whenever I try to do something like
ratings.map()
I get a TypeError in the console saying ratings.Map is not a function
.
I'm aware that the fetch
library returns data asynchronously, but all I really want is for the PromiseResult
to be stored in a useState()
hook so I can perform operations on it further down the line.
Upvotes: 0
Views: 2531
Reputation: 27285
Because the fetch runs asynchronously, you're not going to be able to initialize your state using the immediate result of invoking fetchRatings.
There are, fortunately, a couple of fairly straightforward ways to handle this. You can initialize your state with an empty value and then update it once fetchResults resolves:
function DisplayPage() {
// initially ratings will be undefined
const [ratings, setRatings] = useState();
// update state when fetchResults comes back
fetchResults().then(response => setRatings(response));
// ...
The above example omits this in the interest of readability, but you'd generally do this via useEffect so it runs when your component mounts or when relevant inputs (usually props, known as dependencies for the effect) change:
function DisplayPage() {
const [ratings, setRatings] = useState();
useEffect(() => {
fetchResults().then(response => setRatings(response));
}, []) // empty dependencies array will cause the effect to run only once, on mount
// ...
Upvotes: 0
Reputation: 148
I would advice using the useEffect hook to set initial state.(similar to componentDidMount)
So if the response you expect is for example an array.
const [ratings, setRatings] = useState([]);
Then in the useEffect hook, update state when you get a response from your fetch request. That way you can prevent errors if you for example map over ratings in your DOM somewhere before the request is finished.
useEffect(){
fetch(ratingsUrl, {
method: "GET",
headers: new Headers({
Authorization: "Basic " + base64.encode(login + ":" + password),
}),
})
.then((response) => {
response.json()
})
.then(res => setRatings(res))
.catch(handleError);
Upvotes: 0
Reputation: 73
How About this ,
const [ratings, setRatings] = useState();
useEffect(()=>{
fetch(ratingsUrl, {
method: "GET",
headers: new Headers({
Authorization: "Basic " + base64.encode(login + ":" + password),
})}).then((response) => {let res = response.json();
setRatings(res)
})
.catch(handleError);
},[])
Upvotes: 0
Reputation: 99861
async
methods return promises. If you directly set the result of a promise in your setRatings
state variable, you will get a promise.
Typically this would be rewritten something like this:
function DisplayPage(){
const [ratings, setRatings] = useState(null);
useEffect(() => {
fetchRatings
.then(result => setRatings(result))
.catch(err => console.error(err));
}, []);
if (ratings === null) return <div>loading...</div>;
/* .. do your thing .. */
}
Upvotes: 2