React How to fetch data from mutiple urls - reactjs

From Starships I get only array of urls I would like to fetch these url adresses and get name of each starship. I tried to do it throuht map array but I probably donĀ“t know to write it. Do you have any ideas?
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props){
super(props);
this.state = {
movies:[],
starships:[],
}
}
//we fetch all data and store it to movies
async componentDidMount() {
return await fetch("https://swapi.dev/api/films/")
.then(result => result.json())
.then(data =>
this.setState({
movies: data.results
})
)
}
render(){
console.log(this.state.movies);
return (
<div className="App">
<h1>Movies</h1>
<div className="moviesList">
{this.state.movies.map((movie, index) => {
return <p key={index} >{movie.title}</p>
})}
</div>
<div className="starshipsList">
{this.state.movies.map((starship, index) => {
return <p key={index} >{starship.starships}</p>
})}
</div>
</div>
);
}
}
export default App;

You should check if the API doesn't provide another endpoint for starships as what are you trying to do isn't a best approach in my opinion.
But if it doesn't and you still need starship list, something like this could work:
async componentDidMount() {
// get movies
const moviesResult = await fetch("https://swapi.dev/api/films/");
const movies = await moviesResult.json();
// get array of all urls
const starshipsUrls = movies.results.reduce((acc, movie) => {
return acc.concat(movie.starships);
},[]);
// map over the urls and use promise.all to fetch
const starshipsResult = await Promise.all(starshipsUrls.map(url=> fetch(url)));
const starships = starshipsResult.json();
this.setState({
movies,
starships
});
}

Related

React fetch api data not being displayed

I have a simple fetch request that runs only on the first render. When I update the state with the data and try to map through it nothing is shown. For reference the data I am given back is just one entry. Using .map it should for each entry ( which is 1) display a bullet but nothing is shown.
{quote: "I'm the new Moses"}
import logo from './logo.svg';
import {useState,useEffect} from 'react';
import './App.css';
function App() {
const [users, setUsers] = useState([])
const fetchData = () => {
fetch("https://api.kanye.rest/")
.then(response => {
return response.json()
})
.then(data => {
console.log(data)
setUsers(data)
})
}
useEffect(() => {
fetchData()
}, [])
return (
<div className="App">
<body className='App'>
<div>
{users.length > 0 && (
<ul>
{users.map(user => (
<li>{user.quote}</li>
))}
</ul>
)}
</div>
</body>
</div>
);
}
export default App;
The response value {quote: "I'm the new Moses"} is an object, not an array. It can't be mapped.
Place the response value in an array when saving.
const fetchData = () => {
fetch("https://api.kanye.rest/")
.then(response => {
return response.json();
})
.then(data => {
console.log(data);
setUsers([data]);
});
};
The result you got isn't an array, it's an object. It can't be mapped. However, you can achieve your expected behaviour in many ways. One of them is just put data into an array while setting user.
setUsers([data])

Component not re-rendering after Context change

"myGroups" is a context variable. In my context store, I am fetching data from a database and populating this "myGroups" variable. So initially it contains only an empty array, after some time it contains an array of objects. e.g. [{id: "", data: ""}]
I want to render these groups. So I am mapping through the myGroups variable, and trying to render them.
But the problem is that even after context updating, my component does not re-render. I have console logged and seen that the fetching of data works absolutely fine, though it takes some time to do so.
Does changing the context does not rerender it's consumer? Why is the component not rerendering? It would be of great help if you can provide some solution. Thanks in Advance.
Here is my code.
import React, { useContext, useEffect, useState } from 'react';
import "../css/MyGroups.css";
import GroupCard from './GroupCard';
import { GlobalContext } from '../context/GlobalState';
const MyGroups = () => {
const { myGroups } = useContext(GlobalContext);
useEffect(() => console.log(myGroups), [myGroups]); // Debugging
return (
<div className="my__groups">
<h1 className="my__groups__heading">My Groups</h1>
<div className="my__groups__underline"></div>
<div className="my__groups__grid__container">
{
myGroups.map(({id, data}) => (
<GroupCard
key={id}
name={data.name}
image={data.image}
/>
))
}
</div>
</div>
)
}
export default MyGroups
This is what I get on the console when the context changes:
Console log image
My Global Provider:
<GlobalProvider>
<BrowserRouter>
<Main />
</BrowserRouter>
</GlobalProvider>
The MyGroups Component is a descendant of the Main Component.
Edit 1: Fetch Function of my Store
function fetchGroupsFromDatabase(id) {
let myGroups = [];
db.collection("users").doc(id).get() // Fetch user details with given id
.then(doc => {
doc.data().groupIDs.map(groupID => { // Fetch all group IDs of the user
db.collection("groups").doc(groupID).get() // Fetch all the groups
.then(doc => {
myGroups.push({id: doc.id, data: doc.data()})
})
})
})
.then(() => {
const action = {
type: FETCH_GROUPS_FROM_DATABASE,
payload: myGroups
};
dispatch(action);
})
}
Edit 2: Reducer
const Reducer = (state, action) => {
switch(action.type) {
case FETCH_GROUPS_FROM_DATABASE:
return {
...state,
myGroups: action.payload
};
default:
return state;
}
}
export default Reducer;
As Yousaf said, not need to using useState and useEffect. You can use context in two different way
first:
const MyGroups = () => (
<GlobalContext.Consumer>
{({ myGroups }) => (
<div className="my__groups">
<h1 className="my__groups__heading">My Groups</h1>
<div className="my__groups__underline"></div>
<div className="my__groups__grid__container">
{myGroups.map(({id, data}) => (
<GroupCard
key={id}
name={data.name}
image={data.image}
/>
))
}
</div>
</div>
)}
</GlobalContext.Consumer>
);
second:
const MyGroups = () => {
const { myGroups } = useContext(GlobalContext);
return (
<div className="my__groups">
<h1 className="my__groups__heading">My Groups</h1>
<div className="my__groups__underline"></div>
<div className="my__groups__grid__container">
{myGroups.map(({ id, data }) => (
<GroupCard key={id} name={data.name} image={data.image} />
))}
</div>
</div>
);
};
Edit:
the problem comes from the Fetch Function it based on this answer should be like this:
async function fetchGroupsFromDatabase(id) {
const doc = await db.collection("users").doc(id).get() // Fetch user details with given id
const myGroups = await Promise.all(
doc.data().groupIDs.map(groupID => // Fetch all group IDs of the user
db.collection("groups").doc(groupID).get() // Fetch all the groups
.then(doc => ({ id: doc.id, data: doc.data() }))
)
);
const action = {
type: FETCH_GROUPS_FROM_DATABASE,
payload: myGroups
};
dispatch(action);
}

How to go through multidimensionnal array from API with Reactjs

After many attempts I fail to use arrays from https://swapi.co/api/
What I want is to use data from people and films.
I have 2 files :
App.js
import React, { Component } from "react";
import List from './List';
const API = 'https://swapi.co/api/';
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
componentWillMount() {
this.fetchData();
}
fetchData = async () => {
const response = await fetch(API);
const json = await response.json();
this.setState({
data: json.data
});
};
render() {
return (
<List data={this.state} />
);
}
}
List.js
import React, { Component } from 'react';
import Person from './Person';
class List extends Component {
render() {
const { data } = this.props;
const { results } = data;
return (
<div className="flex-grow-1">
<div className="row mb-5">{results}</div>
</div>
);
}
}
export default List;
So, how do I go through that array to get what data I want to display ? I'd like to render people -> results and films -> results
results would be undefined since results is not a node within your data object... try removing the line const {results} = data and in the return map the data array:
return (
<div className="flex-grow-1">
{
data.map((results, i) => {
return (<div key={i} className="row mb-5">{results}</div>);
})
}
</div>
);
you will need the key to avoid React's unique key warning
So this code as it is now will fetch the single object that acts as a map of other urls for entities in this API. what you want to do is modify fetchData so it accepts a url. At that moment you can do an initial request to /api, read the url from the result for people and films and call fetchData again with these urls. The received data can be saved inside the state.
A example for the implementation of componentWillMount() and fetchData():
componentWillMount() {
this.fetchData('https://swapi.co/api/')
.then(res => {
this.fetchData(res.people).then(people => alert(JSON.stringify(people)));
this.fetchData(res.films).then(people => alert(JSON.stringify(people)));
});
}
async fetchData(url) {
const response = await fetch(url);
return response.json();
};
The most important change is that fetchData now returns a promise. That allows us to use the then method to use the result of the first request. You can replace the alert with a setState implementation for yourself.

React, data display with GitHub api

I am trying to display a list of user repositories. Through the spread operator attempts to spell the object. However, I do not know if this is a good method, there are no errors in the console, but nothing appears on the screen. This is my code.
class ItemUserDetail extends React.Component {
constructor() {
super();
this.state = {
usersRepos: []
};
}
componentDidMount() {
const { user } = this.props.match.params;
const url = `https://api.github.com/users/${user}/repos`;
fetch(url)
.then(res => res.json())
.then(json => this.setState({ usersRepos: json }));
}
render() {
const Repos = this.state.usersRepos ? { ...this.state.usersRepos } : null;
return (
<div>
<p>{Repos.name}</p>
</div>
);
}
}
export default ItemUserDetail;
Since you are returning an array of repositories, your render method should look like this
render() {
const Repos = this.state.usersRepos ? this.state.usersRepos : null; // you don't need this
const { userRepos } = this.state; // destructure
return (
<div>
{userRepos.map(repo => <p key={repo.id}>{repo.name}</p>)}
</div>
);
}

Mapping over array and rendering result

I am having difficulties mapping over this object in order to render the data on the API.
Anyone has an idea of what I may be doing wrong?
class HomePage extends Component {
state = {
weatherResults: []
};
componentDidMount() {
let obj;
fetch(`http://api.openweathermap.org/data/2.5/forecast?
id=52490&appid=${API_KEY}&q=new%20york&cnt=2`)
.then(res => res.json())
.then(results => (obj = results))
.then(() => console.log(obj));
this.setState({
weatherResults: this.state.weatherResults
});
}
render() {
return (
<div>
{this.state.weatherResults &&
this.state.weatherResults.map(data => (
<div className="container">
<p>{data.city.name}</p>
</div>
))}
</div>
);
}
}
export default HomePage;
Since the fetch request is asynchronous, you want to use setState when the request has finished to put the response in the component state.
Looking at one of the sample requests of the API it looks like you get an object as response, which has city.name in it. Instead of having weatherResults be an array, you can set it to null initially and access city.name on it when the object has loaded.
Example
class HomePage extends Component {
state = {
weatherResults: null
};
componentDidMount() {
fetch(`http://api.openweathermap.org/data/2.5/forecast?id=52490&appid=${API_KEY}&q=new%20york&cnt=2`)
.then(res => res.json())
.then(results => {
this.setState({
weatherResults: results
});
})
.catch(error => console.error(error));
}
render() {
const { weatherResults } = this.state;
if (weatherResults === null) {
return null;
}
return (
<div>
<div className="container">
<p>{weatherResults.city.name}</p>
</div>
</div>
);
}
}

Resources