How to pass API data to child components - javascript

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.

Related

Dynamically create options from a dropdown select menu in react

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!

How do I call JSON data stored as a variable in a different component?

I am currently drawing JSON data from my api using axios, and I am mapping this data and storing it as a variable. I want to be able to call these variables in my react components, but I can't seem to figure out the best way.
Getting the JSON data and storing as a variable
function ProfileOne(){
const [profiles, setProfiles] = useState([])
useEffect(() => {
axios.get("api/profiles/")
.then(res =>{
console.log(res)
setProfiles(res.data)
})
.catch(err => {
console.log(err)
})
}, [])
return (
profiles.map(profile => {
const { name } = profile;
})
<div>
<h2><b>{profile.name}</b></h2>
</div>
)
}
And I want to be able to call something like profile.major in a react component, but the way I am currently trying to do it does not work. Please let me know the correct way to do this. Thank you in advance.
If you're going to pass data from component to component you may have to restructure your application or implement a state management library such as redux. Personally I would move your API call to a parent component and then pass the data as props to child components. Something like the following for the parent component:
function ParentComponent() {
const [profiles, setProfiles] = useState([])
useEffect(() => {
axios.get("api/profiles/")
.then(res =>{
console.log(res)
setProfiles(res.data)
})
.catch(err => {
console.log(err)
})
}, [])
return (
<>
<ProfileOne profiles={profiles} />
<OtherComponent profiles={profiles} />
</>
);
}
And in the child component:
function ProfileOne(props){
return props.profiles.map(profile => (
<div>
<h2><b>{profile.name}</b></h2>
</div>
)
}

Objects are not valid as a React child while setting hook after promise.all(responses)?

I am trying to call 3 api's using promise.all() as per below code after completion of promises trying to update those responses to my state hook using useEffect
import React from "react";
const [responses,setResponses] = React.useEffect([])
const filterResolveData = () => {
const filterTypes = ["grade", "subject", "type"];
const promises = filterTypes.map((ele) => {
return getFilterData(ele).catch((err) => console.log(err)); //api calls
});
Promise.all(promises)
.then((response) => {
console.log("allPromises", response);
setFilterData(response); // Issue here tried using cloning but didn't work!
})
.catch((error) => console.log(`Error in executing ${error}`));
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.production.min.js"></script>
Can anyone help me out where I am doing wrong
Issue lies on JSX (HTML) where we need to iterate over an array.
grades.map((item, index) => {
return (
<div
key={index}
className="subMenu"
data-value={item.filterType}
data-shows={item.filterValue}
>
<h6>{item.filterValue}</h6> //Instead I used {item}
</div>
);

Can I use two useEffect and have map inside a map

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;

How to map over a response from a REST call? [duplicate]

This question already has answers here:
what is right way to do API call in react js?
(14 answers)
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
I have the following code where I am making a REST call and assigning the result to a variable.
Then I am using the result to map over and create components with props.
But at present it throws an error because the value for list is undefined.
I believe this is because the value of the list is not set yet when I am attempting to map due to axios async call not completed yet.
Thus 2 queries.
How should I use the response value. Is my method of assigning it to the variable 'list' correct or it should be done differently?
How do I wait for list to be populated and then map over it?
You can see how the response.data will look by looking at following endpoint: https://sampledata.free.beeceptor.com/data1
Sample response data:
[
{
"word": "Word of the Day",
"benevolent": "be nev o lent",
"adjective": "adjective",
"quote": "well meaning and kindly.<br/>a benevolent smile",
"learn": "LEARN MORE"
},
{
"word": "Word of the Day",
"benevolent": "be nev o lent",
"adjective": "adjective",
"quote": "well meaning and kindly.<br/>a benevolent smile",
"learn": "LEARN MORE"
}
]
Client code:
const App = () => {
// const cardData = useSelector(state => state.cardData)
let list;
useEffect(() => {
axios.get('https://sampledata.free.beeceptor.com/data1')
.then(response => {
list = response.data;
list.forEach(l => console.log(l))
})
.catch(error => {
console.log(error)
})
}, [])
return (
<>
<ButtonAppBar/>
<div className='container'>
<div className='row'>
{
list.map((data) => {
const {word, bene, adj, well, learn} = data;
return (
<div className='col-lg-3 col-md-6 format'>
<SimpleCard word={word} bene={bene} adj={adj} well={well} learn={learn} />
</div>
)
})
}
</div>
</div>
</>
);
}
export default App;
You need to make use of useState to store the data that you get from the API.
For example
const [state, setState] = useState({ list: [], error: undefined })
Because the API call is asynchronous and the data will not be available until the component mounts for the first time. You need to use a conditional to check for state.list.length otherwise it will throw an error cannot read property ..x of undefined.
const App = () => {
// create a state variable to store the data using useState
const [state, setState] = useState({ list: [], error: undefined });
useEffect(() => {
axios
.get("https://sampledata.free.beeceptor.com/data1")
.then(response => {
setState(prevState => ({
...prevState,
list: [...prevState.list, ...response.data]
}));
})
.catch(error => {
setState(prevState => ({ ...prevState, list: [], error: error }));
});
}, []);
return (
<>
<ButtonAppBar/>
<div className='container'>
{
// you can show a loading indicator while your data loads
!state.list.length && <div>The data is loading....</div>
}
<div className='row'>
{
state.list.length && state.list.map((data) => {
const {word, bene, adj, well, learn} = data;
return (
<div className='col-lg-3 col-md-6 format'>
<SimpleCard word={word} bene={bene} adj={adj} well={well} learn={learn} />
</div>
)
})
}
</div>
</div>
</>
);
}
You could benefit from using useState hook here.
For example:
const App = () => {
const [list, setList] = useState([]);
useEffect(() => {
axios.get('https://sampledata.free.beeceptor.com/data1')
.then(response => {
setList(response.data);
})
.catch(error => {
console.log(error)
})
}, [])
return (
<>
<ButtonAppBar/>
<div className='container'>
<div className='row'>
{
list.map((data) => {
const {word, bene, adj, well, learn} = data;
return (
<div className='col-lg-3 col-md-6 format'>
<SimpleCard word={word} bene={bene} adj={adj} well={well} learn={learn} />
</div>
)
})
}
</div>
</div>
</>
);
}
export default App;
Do not use let to save fetched values instead use state or props in case you want to generate UI from that. In react component rerender if state or props value changed.
Reason of getting error is, you are doing asynchronous call and because of that your component is parallely rendering and inside the return list will be null and it will throw error .
Correct way is :
const App = () => {
const [list, setlist]= React.useState([])
useEffect(() => {
axios.get('https://sampledata.free.beeceptor.com/data1')
.then(response => {
setlist (response.data)
})
.catch(error => {
console.log(error)
})
}, [])
return (
<>
<ButtonAppBar/>
<div className='container'>
<div className='row'>
{
list.map((data) => {
const {word, bene, adj, well, learn} = data;
return (
<div className='col-lg-3 col-md-6 format'>
<SimpleCard word={word} bene={bene} adj={adj} well={well} learn={learn} />
</div>
)
})
}
</div>
</div>
</>
);
}
export default App;
This can be solved in two ways (since you are using hooks)
useRef() (I would not recommend doing this)
useState() (as the example I have given)
I will show you by using the useState method, but you should keep in mind that since it's a state it will re-render (I don't think it will be an issue here).
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const App = () => {
let [list, setList] = useState(<>LOADING</>);
useEffect(() => {
// You can use your link here
// I have created corsenabled.herokuapp.com just to bypass the CORS issue. It's only for testing and educational purpose only. No intention to infringe any copyrights or other legal matters
// I have used jsonplaceholder.typicode.com as an example
axios.get('https://corsenabled.herokuapp.com/get?to=https://jsonplaceholder.typicode.com/posts')
.then(response => {
let tempData = response.data;
let anotherData = tempData.map(data => {
return (<div>{data.userId}<br/>{data.id}<br/>{data.title}<br/>{data.body} <br/><br/></div>)
})
// tempData = tempData.map(data => <div> {JSON.stringify(data)} </div>)
console.log(tempData)
setList(anotherData)
})
.catch(error => {
console.log(error)
})
}, [])
return (
<>
<div className='container'>
<div className='row'>
{
list
}
</div>
</div>
</>
);
}
export default App;

Categories

Resources