So, I'm trying to dynamically create the options of a select dropdown, I make the fetch of an api with the states of my country, but I don't know how to access the content inside each object..
As you can see below, the data is being pulled from the API, that is, the fetch worked, but I don't know how to create the options that will be inside the Select with each object..
import { EmailIcon, LocationIcon } from './assets/FormSvgIcons'
import { useEffect, useState } from 'react';
const SettingsForm = () => {
const [stateList, setStateList] = useState([]);
const [userLocation, setUserLocation] = useState('');
const handleLocation = () => {
setUserLocation(e.target.value);
}
useEffect(() => {
let initialStates = [];
fetch('https://servicodados.ibge.gov.br/api/v1/localidades/estados/')
.then(response => {
return response.json();
}).then(data => {
initialStates = data.map((states) => {
return states
});
console.log(initialStates);
setStateList({states: initialStates});
});
}, []);
const createDropdownOptions = () => {
const createOptions = stateList.map((state, i) => {
Object.keys(state).map(singleState => (
<option value={i}>{singleState.sigla}</option>
))
});
return createOptions;
}
return (
<form>
<div className="user-country">
<label className="white-label">
Local
</label>
<div className="input-icon-wrapper">
<div className="icon-input w-embed">
<LocationIcon />
</div>
<select
className="select-field white-select w-select"
id="locationField"
name="locationField"
onChange={handleLocation}
>
{createDropdownOptions()}
</select>
</div>
</div>
</form>
)
I know that the error is in the createDropdownOptions function because it is responsible for creating the options, but I don't know how to do it, any light?
I see your problem, your logic is correct, but it is poorly implemented, once you have filtered the data, it is only rendering a new component:
import { EmailIcon, LocationIcon } from "./assets/FormSvgIcons";
import React, { useEffect, useState } from "react";
export default function SettingsForm() {
const [stateList, setStateList] = useState([]);
useEffect(() => {
fetch("https://servicodados.ibge.gov.br/api/v1/localidades/estados/")
.then((response) => {
return response.json();
})
.then((data) => {
console.log(data);
setStateList(data);
});
}, []);
return (
<form>
<div className="user-country">
<label className="white-label">Local</label>
<div className="input-icon-wrapper">
<div className="icon-input w-embed">
<LocationIcon />
</div>
<select
className="select-field white-select w-select"
id="locationField"
name="locationField"
onChange={handleLocation}
>
{stateList.map((state) => {
return <CreateDropdownOptions state={state} />;
})}
</select>
</div>
</div>
</form>
);
}
function CreateDropdownOptions({ state }) {
return (
<option key={state.id} value={state.sigla}>
{state.sigla}
</option>
);
}
I recommend using a component for each option, this will make it easier if you later need to do some action on the
First you could simplify your useEffect to the code below. As you are making a map where the callback returns the same object for each iteration, better you use data as it's, because the output would be the same.
useEffect(() => {
fetch("https://servicodados.ibge.gov.br/api/v1/localidades/estados/")
.then((response) => {
return response.json();
})
.then((data) => {
console.log(data);
setStateList(data);
});
}, []);
Then change createDropdownOptions to the code below. You can change the value or what's displayed to nome:
const createDropdownOptions = () => {
const createOptions = stateList.map((state) => (
<option key={state.id} value={state.sigla}>
{state.sigla}
</option>
));
return createOptions;
};
And finnaly you would need to pass the event to handleLocation:
const handleLocation = (e) => {
setUserLocation(e.target.value);
}
Don't overthink. Tips:
Keep your fetching logic as simple as possible.
Prefer Async Await instead of then chaining for readability.
Honor your state initialization. If you said it is an Array, don't set it as an object.
If you have an array, you can easily map it into jsx and generate your options.
You did very well, and got really close. Take a look at the changes I've done to get it working:
import { useEffect, useState } from 'react';
export const SettingsForm = () => {
const [stateList, setStateList] = useState([]);
const [userLocation, setUserLocation] = useState('');
const handleLocation = () => {
setUserLocation(e.target.value);
};
useEffect(() => {
const loadOptions = async () => {
const data = await fetch(
'https://servicodados.ibge.gov.br/api/v1/localidades/estados/'
).then((response) => {
return response.json();
});
setStateList(data);
};
loadOptions();
}, []);
return (
<form>
<div className="user-country">
<label className="white-label">Local</label>
<div className="input-icon-wrapper">
<div className="icon-input w-embed"></div>
<select
className="select-field white-select w-select"
id="locationField"
name="locationField"
onChange={handleLocation}
>
{stateList.map((state) => {
return (
<option key={state.nome} value={state.nome}>
{state.sigla}
</option>
);
})}
</select>
</div>
</div>
</form>
);
};
Hope it helps! keep up the good work and feel free to reach out in case you're still stuck!
Related
I'm currently working on a project with the Pokemon API and i'm facing a problem.
I want to change the value parameter in the async function getPokemonTypes(), but the value I receive in handleSelect() is not working.
On the console.log(value), the value changes every time I select a different option.
Could someone tell me what I'm doing wrong?
import React from 'react'
import { useState, useEffect } from "react";
import { Link } from 'react-router-dom'
async function getPokemonTypes(value) {
const response = await fetch(`https://pokeapi.co/api/v2/type/${value}`)
const data = await response.json()
console.log(data)
return data
}
async function getPokemonInfo(pokemonId) {
const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${pokemonId}`)
const data = await response.json()
return data
}
export const PokemonTypesCard = () => {
const [pokemonTypesCard, setPokemonTypesCard] = useState({})
const [pokemonIdNumber, setPokemonIdNumber] = useState([])
const [value, setValue] = useState('normal')
const handleSelect = (value) => {
setValue(value)
}
console.log(value)
useEffect(() => {
async function fetchData() {
const pokemonTypesCard = await getPokemonTypes(value)
const pokemonIdText = pokemonTypesCard.pokemon.map((item) => {
return item.pokemon.name
})
const data = pokemonIdText.map(async (pokemonId) => {
return (
await getPokemonInfo(pokemonId)
)
})
const pokemonIdNumber = await Promise.all(data)
setPokemonIdNumber(pokemonIdNumber)
setPokemonTypesCard(pokemonTypesCard)
}
fetchData()
}, [])
return (
<section>
<div>
<label htmlFor='pokemon-types'>Choose a pokemon type</label>
<form>
<select onChange={(event) => handleSelect(event.target.value)}
value={value}>
<option value='normal'>Normal</option>
<option value='fighting'>Fighting</option>
<option value='flying'>Flying</option>
<option value='poison'>Poison</option>
<option value='ground'>Ground</option>
<option value='rock'>Rock</option>
<option value='bug'>Bug</option>
<option value='ghost'>Ghost</option>
<option value='steel'>Steel</option>
<option value='fire'>Fire</option>
<option value='water'>Water</option>
<option value='grass'>Grass</option>
<option value='electric'>Electric</option>
<option value='psychic'>Psychic</option>
<option value='ice'>Ice</option>
<option value='dragon'>Dragon</option>
<option value='dark'>Dark</option>
<option value='fairy'>Fairy</option>
<option value='shadow'>Shadow</option>
</select>
</form>
</div>
{<div>
<ul>
{!pokemonIdNumber ? '' : pokemonIdNumber.map((item, index) =>
<li key={index}>
<Link to={`/pokemon/${item.id}`}>
<img
src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${item.id}.png`}
alt={item.name}
/>
</Link>
<p>{item.name}</p>
</li>
)}
</ul>
</div>}
</section>
);
}
You need to add type to the dependnecies array of useEffect:
useEffect(() => {
async function fetchData() {
const pokemonTypesCard = await getPokemonTypes(value);
const pokemonIdText = pokemonTypesCard.pokemon.map((item); => {
return item.pokemon.name;
});
const data = pokemonIdText.map(async (pokemonId) => {
return (
await getPokemonInfo(pokemonId)
);
});
const pokemonIdNumber = await Promise.all(data);
setPokemonIdNumber(pokemonIdNumber);
setPokemonTypesCard(pokemonTypesCard);
}
fetchData();
}, [value]); // <= HERE
Keep in mind this code has some issues, as you might end up seeing data for a type that doesn't match the one in the URL if something like this happens:
You select fire and getPokemonTypes('fire') is called.
You select to ice and getPokemonTypes('ice') is called.
getPokemonTypes('ice') finishes loading and the rest of the fetchData function executes.
getPokemonTypes('fire') finishes loading and the rest of the fetchData function executes.
The selected option is now ice but see data from fire.
The proper way to do it would be like this:
useEffect(() => {
let shouldUpdate = true;
async function fetchData() {
const pokemonTypesCard = await getPokemonTypes(value);
if (!shouldUpdate) return;
const pokemonIdText = pokemonTypesCard.pokemon.map((item) => {
return item.pokemon.name;
});
const data = pokemonIdText.map((pokemonId) => {
return getPokemonInfo(pokemonId);
});
const pokemonIdNumber = await Promise.all(data);
if (!shouldUpdate) return;
setPokemonIdNumber(pokemonIdNumber);
setPokemonTypesCard(pokemonTypesCard);
}
fetchData();
// This cleanup function will be called if the `useEffect`'s
// dependencies change, so if you run it twice in a row for
// different types, when the result of the first one arrives,
// it will be discarded:
return () => {
shouldUpdate = false;
};
}, [value]);
Also, you have an error here:
const data = pokemonIdText.map(async (pokemonId) => {
return (
await getPokemonInfo(pokemonId)
);
});
const pokemonIdNumber = await Promise.all(data);
You want to store all those promises in the data array and await them all together with Promise.all below, but you are instead awaiting them one by one. It should be like this:
const data = pokemonIdText.map((pokemonId) => {
return getPokemonInfo(pokemonId);
});
const pokemonIdNumber = await Promise.all(data);
I'm working on a React homework assignment, working with the pokemon API. In this particular component, I'm accessing the API to return a list of all the pokemon names and render them to the browser.
I've called the API and mapped it out, and it seems to work when I console.log the names, but I'm not sure what I'm doing wrong that it is not rendering onto the actual browser, and could definitely use some help. Code:
import React, { useEffect, useState } from "react";
function PokedexHome(){
const [pokedexList, setPokedexList] = useState(undefined);
const [isLoading, setIsLoading] = useState(true);
const [hasError, setHasError] = useState(false);
useEffect(() => {
fetch(`https://pokeapi.co/api/v2/pokemon/?limit=100&offset=0`)
.then(response => response.json())
.then(data => {
setPokedexList(data)
setIsLoading(false);
},
error => {
setHasError(true)
setIsLoading(false)
}
);
},[]);
if(isLoading){
return <p>Loading...</p>
}
if(hasError){
return <p>An error has occurred, please try again later</p>
}
pokedexList.results.map((pokemon) => {
console.log(pokemon.name)
return <div className="list-container">
<p>{pokemon.name}</p>
</div>
})
};
export default PokedexHome
if you have a list then your PokedexHome returns void :)
so, first of all, you are missing a return before the map.
second, (if nothing changed lately) you can't return an array of components, you need to return a single component, which can be a Fragment (a React component without UI representation, created for this purpose)
return (
<>
{
pokedexList.results.map((pokemon) => {
console.log(pokemon.name)
return <div className="list-container">
<p>{pokemon.name}</p>
</div>
})
}
</>
)
You miss the return keyword in front if pokedexList. It should be like this. And also add () on your return
return pokedexList.results.map((pokemon) => {
return (
<div className="list-container">
<p>{pokemon.name}</p>
</div>
)
})
};
Your component need to return a JSX, so add return and wrap your list with <></>.
import React, { useEffect, useState } from 'react';
function PokedexHome() {
const [pokedexList, setPokedexList] = useState(undefined);
const [isLoading, setIsLoading] = useState(true);
const [hasError, setHasError] = useState(false);
useEffect(() => {
fetch(`https://pokeapi.co/api/v2/pokemon/?limit=100&offset=0`)
.then((response) => response.json())
.then(
(data) => {
setPokedexList(data);
setIsLoading(false);
},
(error) => {
setHasError(true);
setIsLoading(false);
},
);
}, []);
if (isLoading) {
return <p>Loading...</p>;
}
if (hasError) {
return <p>An error has occurred, please try again later</p>;
}
return (
<>
{pokedexList.results.map((pokemon) => {
console.log(pokemon.name);
return (
<div className="list-container">
<p>{pokemon.name}</p>
</div>
);
})}
</>
);
}
The most probable issue I can think of here is, you are not wrapping the whole JSX in () round braces. It specifies that you want to return something. With main function return statement of course
Example code
Try wrapping it like this.
return pokedexList.results.map((pokemon) => (
<div className="list-container">
<p>{pokemon.name}</p>
</div>
))
I am learning React as I am fetching data from Pokéapi to make a list component, card component, detail component and filter component. I am trying to make a filter so you can filter by pokémon type. Only the cards that also contain that type string should then render (Not there yet). So I am not sure if a) I should make a different call from API inside PokemonList depending on selected value or b) if I should compare the values and just change how the PokemonCard element is rendered inside PokemonList.js depending on the comparison. I managed to pass data from filter to the list component. I have then been trying to pass the type data from PokemonCard.js to the list component so that I can compare these two values but I find it hard to use callbacks to pass the type data from the card component, since I dont pass it through an event or something like that.
Which method should I use here to simplify the filtering? Make different API call or render PokemonCard element conditionally?
Is it a good idea to compare filter option to pokemon card's type in PokemonList.js? Then how can I pass that data from the card component since I don't pass it through click event?
Thankful for any ideas! I paste the code from list component that contains the cards, card component and filter component.
PokemonList component:
import { useState } from 'react';
import useSWR from 'swr';
import PokemonCard from './PokemonCard';
import PokemonFilter from './PokemonFilter';
import './PokemonList.css';
const PokemonList = () => {
const [index, setIndex] = useState(0);
const [type, setType] = useState('');
function selectedType(type) { // value from filter dropdown
setType(type)
console.log("handled")
console.log(type)
}
const url = `https://pokeapi.co/api/v2/pokemon?limit=9&offset=${index}`;
const fetcher = (...args) => fetch(...args).then((res) => res.json())
const { data: result, error } = useSWR(url, fetcher);
if (error) return <div>failed to load</div>
if (!result) return <div>loading...</div>
result.results.sort((a, b) => a.name < b.name ? -1 : 1);
return (
<section>
<PokemonFilter onSelectedType={selectedType} selectedPokemonType={type} />
<div className="pokemon-list">
<div className="pokemons">
{result.results.map((pokemon) => (
<PokemonCard key={pokemon.name} pokemon={pokemon} /> // callback needed??
))}
</div>
<div className="pagination">
<button
onClick={() => setIndex(index - 9)}
disabled={result.previous === null}
>
Previous
</button>
<button
onClick={() => setIndex(index + 9)}
disabled={result.next === null}
>
Next
</button>
</div>
</div>
</section>
)
}
export default PokemonList;
PokemonCard component:
import { Link } from "react-router-dom";
import useSWR from 'swr';
import './PokemonCard.css';
const PokemonCard = ({ pokemon }) => {
const { name } = pokemon;
const url = `https://pokeapi.co/api/v2/pokemon/${name}`;
const { data, error } = useSWR(url);
if (error) return <div>failed to load</div>
if (!data) return <div>loading...</div>
const { types, abilities } = data;
// types[0].type.name <---- value I want to pass to PokemonList.js
return (
<div className='pokemon-card'>
<div className='pokemon-card__content'>
<img
className='pokemon-card__image'
src={data.sprites.front_default}
alt={name}
/>
<div className='pokemon-card__info'>
<p className='pokemon-card__name'>Name: {name}</p>
<p className='pokemon-card__abilities'>Abilities: {abilities[0].ability.name}</p>
<p className='pokemon-card__categories'>Category: {types[0].type.name}</p>
</div>
</div>
<Link className='pokemon-card__link' to={{
pathname: `/${name}`,
state: data
}}>
View Details
</Link>
</div>
)
}
export default PokemonCard;
PokemonFilter component:
import './PokemonFilter.css';
import useSWR from 'swr';
const PokemonFilter = ({onSelectedType, selectedPokemonType}) => {
const url = `https://pokeapi.co/api/v2/type/`;
const fetcher = (...args) => fetch(...args).then((res) => res.json())
const { data: result, error } = useSWR(url, fetcher);
if (error) return <div>failed to load</div>
if (!result) return <div>loading...</div>
function filteredTypeHandler(e) {
console.log(e.target.value);
onSelectedType(e.target.value);
}
console.log(selectedPokemonType)
return(
<div className="pokemon-types__sidebar">
<h2>Filter Pokémon by type</h2>
<select
name="pokemon-type"
className="pokemon-types__filter"
onChange={filteredTypeHandler}
>
<option value="All">Filter By Type</option>
{result.results.map((type) => {
return (
<option key={type.name} value={type.name}> {type.name}</option>
)
})}
</select>
</div>
)
}
export default PokemonFilter;
Here is an example to improve, modify, ... I didn't test, it's just a visual example.
I don't know about useSWR sorry, I use axios in my example...
If you want to centralize all your API requests, you can create a useApi hook, on the internet you will find tutorials.
PokemonList.js
import React, { useState, useEffect } from 'react';
import axios from 'axios'; // or swr
import PokemonFilter from './PokemonFilter';
import PokemonCard from './PokemonCard';
export default function PokemonList() {
const [data, setData] = useState([]);
const [filter, setFilter] = useState('');
// Executed every first render
useEffect(() => {
getData();
}, []);
// Executed only when filter changes
useEffect(() => {
getDataByTypes(filter);
}, [filter]);
// Get data
const getData = async () => {
const uri = 'https://xxx';
try {
const response = await axios.get(uri);
setData(response.data...);
} catch (error) {
console.log(error);
}
};
// Get data by types
const getDataByTypes = async (filter) => {
const uri = `https://xxx/type/${filter}...`;
if (filter) {
try {
const response = await axios.get(uri);
setData(response.data...);
} catch (error) {
console.log(error);
}
}
};
return (
<div className="main">
<PokemonFilter filter={filter} setFilter={setFilter} />
<div className="container">
<div className="cards-container">
{data.map((d) => (
<PokemonCard key={d.name} data={d} />
))}
</div>
</div>
</div>
);
}
PokemonCard.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
export default function PokemonCard({ data }) {
const [pokemons, setPokemons] = useState();
useEffect(() => {
getPokemons(data);
}, [data]);
// Get Pokemons
const getPokemons = async (data) => {
const uri = `https://xxx/pokemon/${data.name}/`;
try {
const response = await axios.get(uri);
setPokemons(response.data...);
} catch (error) {
console.log(error);
}
};
return (
<div>
{pokemons && (
<div className="card">
<img src={pokemons.sprites.front_default} alt={pokemons.name} />
<p>{pokemons.name}</p>
<p>{pokemons.abilities[0].ability.name}</p>
<p>{pokemons.types[0].type.name}</p>
</div>
)}
</div>
);
}
PokemonFilter.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
export default function PokemonFilter({ filter, setFilter }) {
const [types, setTypes] = useState([]);
useEffect(() => {
getType();
}, []);
// Get Type
const getType = async () => {
const uri = 'https://xxx/type/';
try {
const response = await axios.get(uri);
setTypes(response.data.results....);
} catch (error) {
console.log(error);
}
};
const handleFilter = (e) => {
setFilter(e.target.value);
};
return (
<select onChange={handleFilter} value={filter}>
<option>Filter by type</option>
{types.map((type) => {
return (
<option key={type.name} value={type.name}>
{type.name}
</option>
);
})}
</select>
);
}
I am trying to create a very simple website called Currency Tracker. I am using the following API: http://api.nbp.pl/. I have managed to get the 'mid' and 'code' data with fetch method which I need to render the child components with. I'm still learning to use API. Unfortunatelly I'm stuck. I don't know how to use this data and how pass it down to render the components.
import MainPageItem from './MainPageItem.js'
import './MainPage.css';
fetch('http://api.nbp.pl/api/exchangerates/tables/A')
.then(response => {
return response.json();
})
.then(data => {
const mid = data[0].rates.map(currency => {
return currency.mid
});
console.log(mid)
})
fetch('http://api.nbp.pl/api/exchangerates/tables/A')
.then(response => {
return response.json();
})
.then(data => {
const code = data[0].rates.map(currency => {
return currency.code
});
console.log(code)
})
function MainPage(props) {
return (
<div className="main-page">
<div className="main-page__currecy-list-container">
<div className="main-page__currency-list">
<MainPageItem currency="EUR" mid="4.5" />
<MainPageItem currency="USD" mid="4" />
<MainPageItem currency="GBP" mid="5" />
<MainPageItem currency="CHF" mid="3.5" />
</div>
</div>
</div>
);
}```
The MainPageItem is just a mockup. How can I pas the 'mid' and 'code' data to props render components?. Ideally I want to
You have to put the api calls inside your MainPage component, store the results inside some state (e.g. using useState) and then passing the data wherever you need, or just using it inside the component itself.
import MainPageItem from './MainPageItem.js'
import { useState, useEffect } from 'react'
import './MainPage.css';
function MainPage(props) {
const [codes, setCodes] = useState([])
const [mids, setMids] = useState([])
useEffect(() => {
fetch('http://api.nbp.pl/api/exchangerates/tables/A')
.then(response => {
return response.json();
})
.then(data => {
const code = data[0].rates.map(currency => {
return currency.code
});
setCodes(code)
})
fetch('http://api.nbp.pl/api/exchangerates/tables/A')
.then(response => {
return response.json();
})
.then(data => {
const mid = data[0].rates.map(currency => {
return currency.mid
});
setMids(mid)
})
}, [])
return (
<div className="main-page">
<div className="main-page__currecy-list-container">
<div className="main-page__currency-list">
{codes.map((code, i) => <MainPageItem key={i} currency={code} mid={mids[i]} />)}
</div>
</div>
</div>
);
}
This is an example of what you can do in order to save the codes inside a piece of state of your component.
The mids part is very naive and I added it only to give you a hint on how to do that.
useing props you can pass data to other component.
I am new to React and would like some help with the following problem. I current have this code.
import React, { useState, useEffect } from "react";
function FetchData() {
const [repos, setRepos] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetch("https://api.github.com/orgs/org_name/repos")
.then((res) => res.json())
.then((data) => {
setRepos(data);
})
.then(() => {
setIsLoading(false);
})
.catch((err) => console.log(err));
}, []);
return (
<div>
{repos.map((repo) => (
<div key={repo.id}>
<div>
<h2>Name: {repo.name}</h2>
<p>Top 5 Contributors</p>
))}
My above codes work fine, but my problem now is that I would like to add the top 5 contributors to the repository and to access that I have to go to https://api.github.com/repos/org_name/{repos}/contributors, and to get to that, I first have to use repo.contributor_url Should I use another useEffect and map to show the top 5 contributors?
Edit
Basically I want to do something like this.
useEffect(() => {
fetch(`${repos.contributors_url}`)
.then((res) => res.json())
.then((data) => {
setContributors(data);
console.log(data);
})
.catch((err) => console.log(err));
}, []);
...
<p> Top 5 Contributors: </p>
<ul>
{contributors.map((c, i) => {
<li key={i}>{c.name}</li>
)}
</ul>
Since you are new to React. React used to have class based components to handle state and those class based components had special functions called- Life-Cycle-Methods. But from React 16.8 onwards React Community came up with React-Hooks and functional components can now be used to handle state and useState() and useEffect() are examples of Hooks.
Now useEffect() alone is used to do perform life-cycle method's work.
The way you have used useEffect() in your code is simulating componentDidMount() as you have kept the 2nd argument as an empty array []
We can use other life-cycle methods like componentDidUpdate() and componetnWillUnmount() using useEffect() Hook itself.
Then based on your requirement you can use useEffect() Hook as many times as required by your Component.
Coming to Updated part of your question now:
So, you basically need to do promise chaining. We know that fetch() is promise based,so when one asynchronous call is resolved and we get the first data, within your useEffect() hook only, you need to make another asynchronous request using the second url-end point to get the respective data.
Here is the updated code now: Try this
import React, { useState, useEffect } from 'react';
function FetchData() {
const [repos, setRepos] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [contributors, setContributors] = useState([]);
const [isContributorLoading, setIsContributorLoading] = useState(true);
useEffect(() => {
fetch('https://api.github.com/orgs/{org}/repos')
.then((res) => res.json())
.then((data) => {
setRepos(data); // Data 1(repos) is received
// Now We make another API call to get Data 2 (contributors)
return fetch('https://api.github.com/repos/{org}/{repos}/contributors');
})
.then((res) => res.json()) // Chaining promise,handling 2nd Fetch request
.then((data2) => {
console.log(data2);
setContributors(data2);
})
.then(() => {
setIsLoading(false);
})
.catch((err) => console.log(err));
}, []);
return (
<div>
{ repos.length && repos.map((repo) => (
<div key={repo.id}>
<div>
<h2>Name: {repo.name}</h2>
</div>
</div>
))}
<p> Top 5 Contributors: </p>
<ul>
{contributors.length && contributors.map((c, i) => {
return <li key={i}>{c.name}</li>
)}
</ul>
</div>
);
}
So, basically you need to learn a bit more about how to use Hooks especially useEffect(), for now. Do some googling stuff, It would not be good if I tell you everything now. Give it a shot then.
You can directly call apis inside one useEffect.
import React, { useState, useEffect } from "react";
function App() {
const [repos, setRepos] = useState([]);
const [contributor, setContributor] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
async function caller() {
try {
setIsLoading(true);
const response = await fetch(
"https://api.github.com/orgs/octokit/repos"
);
const result = await response.json();
const contri = [];
console.log(result);
result.forEach((item) => {
contri.push(fetch(`${item.contributors_url}`));
});
Promise.all(contri)
.then((contributorResults) => contributorResults)
.then((responses) => {
console.log(responses);
return Promise.all(responses.map((r) => r.json()));
})
.then((cont) => {
setContributor([...cont])
});
setRepos(result);
} catch (err) {
console.log(err);
} finally {
setIsLoading(false);
}
}
caller();
}, []);
return (
<div>
{repos.map((repo,index) => (
<div key={repo.id}>
<h2> Name: {repo.name} </h2>
{ contributor[`${index}`] && contributor[`${index}`].slice(0,5).map(item => {
return <div key={item.id}>
<div>{item.login}</div>
</div>
})}
</div>
))}
{isLoading && <div>...loading</div>}
</div>
);
}
export default App;