How to loop through a JSON array with useEffect in React? - json

I am trying to loop through a JSON array with useEffect. When I use the below code (with the array removed from useState) with a single user specified, I am able to get the data. However it does not work with the entire array...any suggestions would be appreciated.
function GitHubUser() {
const [data, setData] = useState([]);
useEffect(() => {
fetch(`https://api.github.com/users/`)
.then(res => res.json())
.then(setData)
.catch(console.error);
}, []);
if (data) {
return (
<div>
<h1>{data.userId}</h1>
<img src={data.avatar_url} width={100} alt={"github avatar"}/>
<p>{data.login}</p>
</div>
);
}
return null;
}

It doen't work because when you do if (data) it returns true because empty array is true
const data = [];
if (data) {
console.log("Empty array is true");
} else {
console.log("Empty array is false");
}
So... when you try to get userId from an empty array it throws an error.
If data is supposed to be an array...
function GitHubUser() {
const [data, setData] = useState([]);
useEffect(() => {
fetch("https://api.github.com/users")
.then((res) => res.json())
.then(setData)
.catch(console.error);
}, []);
if (data) {
return (
<React.Fragment>
{data.map((user) => (
<div>
<h1>{user.userId}</h1>
<img src={user.avatar_url} width={100} alt={"github avatar"} />
<p>{user.login}</p>
</div>
))}
</React.Fragment>
);
}
return null;
}
You have to map each user to the expected output component. Using a map will prevent the previous behaviour because if no elements are present in the array, it will not map anything and return an empty component instead of an error.
If data is not supposed to be an array, then you shouldn't use a map nor initialize the state with an empty array because before running the fetch it will try to render the component and fail.

Is this something like you u want?
I removed the backslash after https://api.github.com/users*here*.
I set the data in useEffect hooks, in your example you didnt do it.
And mapped the array in data, it shows all the github users with
images and names.
.
function GitHubUser() {
const [data, setData] = useState([]);
useEffect(() => {
fetch(`https://api.github.com/users`) // removed the backslash here
.then(res => res.json())
.then(data => setData(data)) // changed this line
.catch(console.error);
}, []);
console.log(data)
return (
<div>
{data.map((item, key) => <> // Mapping through the array here
<h1 key={key}>{item.id}</h1>
<img src={item.avatar_url} width={100} alt={"github avatar"}/>
<p>{item.login}</p>
</>
)}
</div>
);
}

Related

Getting responses from two interdependent JSON generates an error [React Hooks]

I want to download the data first about the city in which the user is located from ipstack.com and then use this information to generate the weather using openweather.com. However, the following problem occurs during render:
Test.js:20 GET https://api.openweathermap.org/data/2.5/weather?lat=undefined&lon=undefined&units=metric&appid=(MY API KEY) 400 (Bad Request)
The problem is strange because the data I require appears in state eventually:
My code:
import React, { useState, useEffect } from 'react';
const Test = () => {
const [place, setPlace] = useState([]);
const [weather, setWeather] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(false)
fetch(`http://api.ipstack.com/check?access_key=(MY API KEY)`)
.then(result => result.json())
.then(json => {
setPlace([json.latitude, json.longitude, json.region_name])
})
.catch(err => {
console.log("Error " + err);
})
}, [])
useEffect(() => {
fetch(`https://api.openweathermap.org/data/2.5/weather?lat=${place[0]}&lon=${place[1]}&units=metric&appid=(MY API KEY)`)
.then(result => result.json())
.then(json => {
setWeather(json)
})
.catch(err => {
console.log("Error " + err);
setLoading(true);
})
}, [place])
return (
<>
{loading && (
<>
<p>{place[0]}</p>
<p>{place[1]}</p>
<p>{place[2]}</p>
<p>{weather.name}</p>
</>
)}
</>
);
}
export default Test;
I think in the second effect you should fetch data from openweathermap.org only if place array assigned and has correct data. Otherwise on first run you will send invalid data (undefineds instead of positions)

How to print json api data in reactjs

I'm fetching json api details through GET request and trying to print it. Getting an error:
Error in the console is Uncaught ReferenceError: allUsers is not defined
const Dashboard = ({status, juser}) => {
const [allUsers, setAllUsers] = React.useState([]);
const id = juser.actable_id;
console.log(id); //getting id here as 1
const getAllusers = () => {
axios
.get(`http://localhost:3001/user/${id}`, { withCredentials: true })
.then((response) => {
console.log(response.data);
setAllUsers(response.data);
})
.catch((error) => {
console.log(" error", error);
});
};
React.useEffect(() => {
getAllusers();
}, []);
{allUsers.map((job_seeker, index) => {
return (
<div>
<p>{job_seeker.name}</p>
</div>
);
})}
}
export default Dashboard;
I'm new to react. Any help is appreciatable.
const [state, setState] = React.useState([]);
the state is where your data is located and setState is function to reset the state from anywhere,
so on your code,
const [jobseekers, allUsers] = React.useState([]); // change string to array
jobseekers is the variable where your data is located and allUsers is the function to store data into state.
set data to state using allUsers function,
const getAllusers = () => {
axios
.get(`http://localhost:3001/user/${id}`, { withCredentials: true })
.then((response) => {
allUsers(response.data);
})
.catch((error) => {
console.log(" error", error);
});
};
and map from jobseekers
{jobseekers.map((job_seeker, index) => {
return (
<div>
<p>{job_seeker.name}</p>
</div>
);
})}
Also I would suggest to rename your state and setState as,
const [allUsers, setAllUsers] = React.useState([]);
You didn't pass the value of response to allUsers, instead, you just created a new variable. So change
const allUsers = response.data;
to:
allUsers(response.data)
Besides, you can also improve the way that you have used useState. You have initialized it as an empty string while you'll probably store an array from response in jobseekers. So, initialize it as an empty array.
const [jobseekers, allUsers] = React.useState([]);

JSON.parse issue with redux state

Using React and Redux, I'm making the call getNoteById which returns some string inside of note.
I want to JSON.parse the note variable but if it's undefined then setValue to initialValue where I know it will parse properly.
The issue I'm having is when I check to see if JSON.parse(note) is undefined or not, I always get the same error of undefined index at 0.
Not sure how to fix this. When the component loads and getNotesById() is called, notes or initialValue should be displayed when i call setValue.
const TextEditor = ({ note, getNoteById, clearValues }) => {
const paramId = match.params.notes;
const [value, setValue] = useState();
useEffect(() => {
getNoteById(paramId);
if (JSON.parse(note) == undefined) {
setValue(initialValue);
} else {
setValue(JSON.parse(note));
}
return () => {
clearValues();
};
}, [paramId, value]);
return (
<div>
<h1> {value} </h1>
</div>
);
};
Your effect has missing dependencies: clearValues, getNoteById and note. You should skip the setValue to initialValue and do const [value, setValue] = useState(initialValue); because an effect runs after the render and note will be undefined on first render. Check if note is undefined with if(note!==undefined){setValue(JSON.parse(note))}.
Your code should look something like this:
const TextEditor = ({ note, getNoteById, clearValues }) => {
const paramId = match.params.notes;
const [value, setValue] = useState(initialValue);
useEffect(() => {
getNoteById(paramId);
if(note!==undefined) {
setValue(JSON.parse(note));
}
return () => {
clearValues();
};
}, [clearValues, getNoteById, note, paramId, value]);
return (
<div>
<h1> {value} </h1>
</div>
);
};
My guess is that you don't want to dispatch getNoteById again when the note is loaded and set in the redux state. Maybe create 2 effects:
const TextEditor = ({ note, getNoteById, clearValues }) => {
const paramId = match.params.notes;
const [value, setValue] = useState(initialValue);
//dispatch getNoteById when param id changes
useEffect(() => {
getNoteById(paramId);
return () => {
//dispatch a cleanup action
clearValues();
};
}, [clearValues, getNoteById, paramId]);
//set the note to it's parsed json when it changes
// and is not undefined
useEffect(() => {
if (note !== undefined) {
setValue(JSON.parse(note));
}
}, [note]);
return (
<div>
<h1> {value} </h1>
</div>
);
};

Why is this promise returning an [object Promise] and not the value?

I thought I fully understood promises, but I'm stumped on this. I realize I should use async/await, but for this example I specifically want to only use .then().
When I do this:
const theJson = fetch(
`https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/quotes.json`
)
.then( quoteTypeResponse => quoteTypeResponse.json() )
.then( data => {
console.log(data)
return data
});
the console.log(data) in the last function prints the JSON as expected, but when I try to console.log(theJson), the returned value, it prints [object Promise].. Why is this?
I was able to get the data outside of the function using react's useState/useEffect but not with just a vanilla global variable. I'm not trying to solve anything, but just want to understand why this does not work.
export default function App() {
let globalVar;
const [theQuote, setTheQuote] = useState({});
useEffect(() => {
fetch(`https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/quotes.json`)
.then(quoteTypeResponse => quoteTypeResponse.json())
.then(quoteType =>
fetch(
'https://programming-quotes-api.herokuapp.com/quotes/' +
quoteType.type
)
)
.then(quoteResponse => {
return quoteResponse.json();
})
.then(quote => {
setTheQuote({ quote: quote.en, author: quote.author });
globalVar = quote.author;
});
}, []);
return (
<div id="app">
<h1>{theQuote.quote}</h1> // renders
<h2>{theQuote.author}</h2> // renders
<h3>globalVar: {globalVar}</h3> // undefined
</div>
);
}
Because your second .then() is inside the first then(), so theJson is a Promise<T>. The nice thing about Promise<T> is that you can move an inner .then() call up a level and it will still work:
Change it from this:
const theJson = fetch(
`https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/quotes.json`
)
.then( quoteTypeResponse => quoteTypeResponse.json().then( data => {
console.log(data)
return data
} )
);
To this:
const theJson = fetch(
`https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/quotes.json`
)
.then( quoteTypeResponse => quoteTypeResponse.json() )
.then( data => {
console.log(data)
return data
});
But ideally, use async function so you can have this considerably simpler code instead:
const resp = await fetch( `https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/quotes.json` );
const data = await resp.json();
console.log( data );
#pushkin left a good link explaining the differences between async/await and using .then(), but basically, the value returned by the then() is only available within that block.
Promises cheat sheet: https://levelup.gitconnected.com/async-await-vs-promises-4fe98d11038f
fetch(`https://s3-us-west-2.amazonaws.com/s.cdpn.io/28963/quotes.json`)
.then(quoteTypeResponse => quoteTypeResponse.json())
.then(quoteType =>
fetch(
'https://programming-quotes-api.herokuapp.com/quotes/' + quoteType.type
)
)
.then(quoteResponse => {
return quoteResponse.json();
})
.then(quote => {
console.log(`q:${util.inspect(quote)}`);
document.getElementById('app').innerHTML = quote.en;
});

how to access nested data by nesting fetch calls?

I'm having trouble understanding the best approach to this.
My goal is to display nested data.
I use fetch on this url - https://horizons-json-cors.s3.amazonaws.com/products.json
which takes me to a page that contains json. inside the json is 3 urls. each url contains the data that I need to get to.
So far, I've accessed the first layer, and now have an array of the item urls. I guess I don't understand how to fetch the data while im inside the outter fetch call.
Here's my code thus far (the result is an array of urls, where each url contains the data I need.) :
componentDidMount() {
console.log('Fetch');
fetch("https://horizons-json-cors.s3.amazonaws.com/products.json")
.then((resp) => (resp.json()))
.then((json) => {
var productUrlArr = [];
for (var i = 0; i < json.length; i++) {
productUrlArr.push(json[i].url);
}
.catch((err) => {
console.log('error', err);
});
}
If you guys could help me out and really walk through how to access the next level of data, I would really, really appreciate it.
You can Fetch Data for Inner URLs this way too,
// 1. Outer Fetch call initiated here
fetch("https://horizons-json-cors.s3.amazonaws.com/products.json")
.then(res => {
return res.json()
})
.then(res => {
// 2. array for storing url's retrieved from response
var urlArray = []
if (res.length > 0) {
// 3. Push url inside urlArray
res.map(data => urlArray.push(data.url))
}
// 4. an array of urls
return urlArray
})
.then(urls => {
// Return an promise which will return "JSON response" array for all URLs.
// Promise.all means “Wait for these things” not “Do these things”.
return Promise.all(urls.map(url => {
// Take url fetch response, return JSON response
return fetch(url).then(res => res.json())
}
))
})
.then(res => {
// Store all objects into array for later use
var objArr = res; return objArr
})
//.then(...)
You have a little error in your code.
It's missing }) before .catch
With it you can use your data in the array.
componentDidMount(){
console.log('Fetch');
fetch("https://horizons-json-cors.s3.amazonaws.com/products.json")
.then((resp) => (resp.json()))
.then((json) => {
var productUrlArr = [];
for (var i = 0; i < json.length; i++) {
productUrlArr.push(json[i].url);
}
console.log(productUrlArr);
}).catch((err) => {
console.log('error', err);
});
}
Hope it helps.
It simple. First get all the url first like you did. Then map and pass it into Promise.all.
fetch("https://horizons-json-cors.s3.amazonaws.com/products.json")
.then((resp) => (resp.json()))
.then((json) => {
Promise.all(json.map(product =>
fetch(product.url).then(resp => resp.text())
)).then(texts => {
// catch all the data
})
}).catch((err) => {
console.log('error', err);
});