get value for api calling from other functions state REACTJS - javascript

So far I am calling an api in componentDidMount() and set it to select option.
also call another conditional api from user input.
But Problem is it is calling the api non stop.
**getRates(){
const base = this.handlePrint(this.state.value);
fetch(`https://exchangeratesapi.io/api/latest?base=${base}`)
.then(data => data.json())
.then(data =>{
this.setState({
rate: data.rates,
})
console.log(data.rates)
})
.catch(err => console.log(err))
}**
And my console screen shot:
console
I just need one time api call based on user input.
full code: https://codeshare.io/5MwXzq
I think there is a problem with the state but I am new in reactjs and could not understand how to solve that.
Anyone can help please.

This is happening not because of anything in componentDidMount()
Based on the code you shared on codeshare.io, you're calling getRates() function in your render() method. Also, you're setting the state using setState within the getRates method. This causes a re-render, calling render() again, and so you get the infinite loop of calls.
Remove the call to getRates() from your render method and it'll work.
EDIT:
Since there were small changes but strewn across your code to get it to work, I've modified your class and posting it here:
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
currencies: [],
value: "?",
base: "?",
input: "?",
rate: 0
};
this.getRates = this.getRates.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handlePrint = this.handlePrint.bind(this);
}
componentDidMount() {
fetch("https://exchangeratesapi.io/api/latest?symbols=USD,GBP,AUD,JPY")
.then(data => data.json())
.then(data => {
const currencies = [];
currencies.push(
data.base,
...Object.entries(data.rates).map(rates => rates[0])
);
this.setState({
currencies
});
})
.catch(err => console.log(err));
}
getRates() {
const base = this.handlePrint();
console.log(this.state); fetch(`https://exchangeratesapi.io/api/latest?base=${base}`)
.then(data => data.json())
.then(data => {
this.setState({
rate: data.rates
});
console.log(data.rates);
})
.catch(err => console.log(err));
}
//Dropdown
DropDown = function(list) {
return <option value={list}>{list}</option>;
};
handleChange(e) {
this.setState({ value: e.target.value });
}
handlePrint() {
console.log(this.state)
if (this.state.value) {
return this.state.value;
}
};
render() {
const { currencies } = this.state;
// const input = this.getRates();
return (
<div>
<span>SELECT your Base: </span>
<select autoFocus onChange={this.handleChange}>
<option inputcurrency={currencies} selected data-default>
SELECT BASE
</option>
{currencies.map(this.DropDown)}
</select>
<button onClick={this.getRates}>GET RAtes</button>
<p>selected base:{this.handlePrint()} </p>
</div>
);
}
}
The changes are:
1. Bound getRates() method in the constructor
2. Removed the call to getRates() in render start
3. Removed unnecessary items passed to handlePrint
4. Changed the button onClick to point to getRates

Related

calling externally componentDidMount() in react

I have a requirement in which once page gets loaded my dropdownlist should be populated. for that I put that code in componentDidMount().
componentDidMount() {
axios.get(`http://localhost:8080/country_code`).then((res) => {
const countryData = res.data;
this.setState({ countryData });
alert(countryData);
});
}
I have one user input field in which person enter the value and save it into database. I want once user save that value into DB, my dropdown should get refresh and that value should be visible in the dropdown. How can I externally call componentDidMount()? is there any better way to handle the same?
As of now list is getting refreshed only when user resfresh the page.
You can't call externally componentDidMount() method !. so you need set
common function which is call in componentDidMount() and onChange dropdown value. see below code !
class App extends Component {
componentDidMount() {
this.handleCallApi();
}
handleCallApi = () => {
axios.get(`http://localhost:8080/country_code`).then((res) => {
const countryData = res.data;
this.setState({ countryData });
alert(countryData);
});
}
render() {
return (
<div>
<button onClick={this.handleCallApi}>Call Api</button>
</div>
);
}
}
export default App;
You can't call componentDidMount externally but you can extract the code in componentDidMount to a method and can call it in both componentDidMount and onSave.
alertDropDown = () => {
axios.get(`http://localhost:8080/country_code`).then((res) => {
const countryData = res.data;
this.setState({ countryData });
alert(countryData);
});
}
componentDidMount
componentDidMount() {
this.alertDropDown()
}
On DB save method
onSave = () => {
this.alertDropDown()
}
You can't call the componentDidMount(), as it's a lifecycle method and is called at initial render. You can expose a function and call that function from inside the componentDidMount() something like:
updateDropdownData = () => {
axios.get(`http://localhost:8080/country_code`).then((res) => {
const countryData = res.data;
this.setState({ countryData });
alert(countryData);
});
}
componentDidMount() {
this.updateDropdownData()
}
And you can call this.updateDropdownData() from anywhere you want. Just like:
onSomeUserAction = () => {
this.updateDropdownData()
}

Consuming Paginated API in React Component

I'm just getting started with React. As a simple exercise, I wanted to create some components for viewing data retrieved from the JsonMonk API. The API contains 83 user records and serves them in pages of 10.
I am trying to develop a component for viewing a list of users one page at a time which I called UserList. The code for it is below:
class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {
pageNumber: 1,
users: [],
};
this.onPageNext = this.onPageNext.bind(this);
}
componentDidMount() {
this.fetchUsers(this.state.pageNumber)
.then((users) => this.setState({users: users}));
}
async fetchUsers(pageNumber) {
const response = await fetch(`https://jsonmonk.com/api/v1/users?page=${pageNumber}`);
const jsonResponse = await response.json();
return jsonResponse.data.records;
}
onPageNext() {
// ...
}
render() {
const postElements = this.state.users.map(
(props) => <User key={props._id} {...props} />);
return (
<div>
{postElements}
<div>
<button onClick={this.onPageNext}>Next</button>
</div>
</div>
);
}
}
The problem I am having pertains to the onPageNext method of my component. When the user clicks the "Next" button, I want to make a fetch for the next page of data and update the list.
My first attempt used an asynchronous arrow function passed to setState like so:
onPageNext() {
this.setState(async (state, props) => {
const nextPageNumber = state.pageNumber + 1;
const users = await this.fetchUsers(nextPageNumber);
return {pageNumber: nextPageNumber, users: users}
})
}
However, it does not seem React supports this behavior because the state is never updated.
Next, I tried to use promise .then syntax like so:
onPageNext() {
const nextPageNumber = this.state.pageNumber + 1;
this.fetchUsers(nextPageNumber)
.then((users) => this.setState({pageNumber: nextPageNumber, users: users}));
}
This works but the problem here is that I am accessing the class's state directly and not through setState's argument so I may receive an incorrect value. Say the user clicks the "Next" button three times quickly, they may not advance three pages.
I have essentially run into a chicken-or-the-egg type problem. I need to pass a callback to setState but I need to know the next page ID to fetch the data which requires calling setState. After studying the docs, I feel like the solution is moving the fetch logic out of the UsersList component, but I'm not entirely sure how to attack it.
As always, any help is appreciated.
You need to change onPageNext as below:
onPageNext() {
this.setState( prevState => {
return {pageNumber: prevState.pageNumber + 1}
}, () =>{
this.fetchUsers(this.state.pageNumber).then(users => this.setState({users: users}) )
});
}
Here is the Complete Code:
import React from "react";
export default class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {
pageNumber: 1,
users: [],
};
this.onPageNext = this.onPageNext.bind(this);
}
componentDidMount() {
this.fetchUsers(this.state.pageNumber)
.then((users) => {
console.log(users, 'users');
this.setState({users: users})
}
);
}
async fetchUsers(pageNumber) {
const response = await fetch(`https://jsonmonk.com/api/v1/users?page=${pageNumber}`);
const jsonResponse = await response.json();
return jsonResponse.data.records;
}
onPageNext() {
this.setState( prevState => {
return {pageNumber: prevState.pageNumber + 1}
}, () =>{
this.fetchUsers(this.state.pageNumber).then(users => this.setState({users: users}) )
});
}
render() {
const postElements = this.state.users.map(
(user) => <User key={user._id} {...user} />);
return (
<div>
{postElements}
<div>
<button onClick={this.onPageNext}>Next</button>
</div>
</div>
);
}
}
function User(props) {
return (
<div>
<div style={{padding: 5}}>Name: {props.first_name} {props.last_name}</div>
<div style={{padding: 5}}>Email: {props.email}</div>
<div style={{padding: 5}}>Phone: {props.mobile_no}</div>
<hr/>
</div>
);
}
Here is the Code Sandbox

React componentDidMount fetch api

I am trying to fetch an api inside componentDidMount. The api result will be set to the component's state and the state mapped and passed to a children component.
If I fetch the api using the fetch method inside the componentDidMount everything works fine:
componentDidMount(){
fetch(apiToFetch)
.then((result) => result.json())
.then((result) => result.entries)
.then((entries) => this.setState({ ...this.state, entries }))
.catch((error) => console.log(error));
}
if I use fetch inside a method and then call this method inside componentDidMount nothing is rendered:
componentDidMount() {
this.fetchApiToEntries(GLOBAL_PORTFOLIO_COLLECTION_API);
}
fetchApiToEntries(apiToFetch) {
fetch(apiToFetch)
.then((result) => result.json())
.then((result) => result.entries)
.then((entries) => this.setState({ ...this.state, entries }))
.catch((error) => console.log(error));
}
I cannot understand what I am missing from the lifecycle.
Shouldn't react do the following?
Init the state
Render
Call componentDidMount
Rerender
Here is my initial component:
export default class Portfolio extends React.Component {
constructor(props) {
super(props);
this.state = {
entries: []
}
}
componentDidMount() {
this.fetchApiToEntries(GLOBAL_PORTFOLIO_COLLECTION_API);
}
fetchApiToEntries(apiToFetch) {
fetch(apiToFetch)
.then((result) => result.json())
.then((result) => result.entries)
.then((entries) => {
this.setState({
...this.state,
entries
})
})
.catch((error) => console.log(error));
}
render() {
return (
<Fade bottom>
<div className="Portfolio">
<div className="Portfolio__title"><h4 className="color--gradient text--spacing">WORKS</h4></div>
<OwlCarousel {...options}>
{this.state.entries.map((item) => (
<PortfolioElement item={item} />
))}
</OwlCarousel>
<AnchorLink href='#contact'><Button className="contact-button btn--gradient slider-button Services__button">Let's get in touch</Button></AnchorLink>
</div>
</Fade>
)
}
}
PortfolioElement is the actual component not being rendered.
Any advice?
Thank you.
Edit: both methods are not rerendering the component the right way (...something I didn't expect: I don't know why but if I call them twice in componentDidMount the component will render the right way). I think I am missing something in the state.
I have no error in the console and this is how I set my initial state:
this.state={entries:[]}
and this is what the actual entries looks like from the console:
entries:
[0:{credits: "..."
description: "..."
featuredImage: {path: "portfolio/01.jpg"}
firstImage: {path: "portfolio/firstimage.jpg"}
secondImage: []
slug: "..."
tasks: (5) [{…}, {…}, {…}, {…}, {…}]
title: "..."
_by: "5beae6553362340b040001ee"
_created: 1542123322
_id: "5beaef3a3362340bf70000b4"
_mby: "5beae6553362340b040001ee"
_modified: 1542149308
},
1:{...}
]
My state after the fetch is the same way.
UPDATE I figured out that the the problem is: when the state changes the component is not rerendering the child with the correct props. I called the API in an higher order component passed down the props and added a componentWillUpdate method forcing a state refresh that rerenders the component. Not the ideal solution but I am not figuring out other ways until now. Any advice?
Idk what your api response is but I tested your code with a fake API and changed
fetchApiToEntries(apiToFetch){}
to Arrow Function (Arrow Function)
fetchApiToEntries = (apiToFetch) => {}
and it's working fine.
Full Example:
export default class Portfolio extends React.Component {
constructor(props) {
super(props);
this.state = {
entries: []
}
}
componentDidMount() {
this.fetchApiToEntries('https://jsonplaceholder.typicode.com/posts');
}
fetchApiToEntries = (apiToFetch) => {
fetch(apiToFetch)
.then(result => result.json())
.then((entries) => {
this.setState({
...this.state,
entries
})
})
.catch((error) => console.log(error));
}
render() {
const {entries} = this.state;
console.log(entries);
return (
// Everything you want to render.
)
}
}
Hope it helps you.
do you need to bind fetchApiToEntries in the constructor or use fat arrows?
this.fetchApiToEntries = this.fetchApiToEntries.bind(this);
sorry I cant comment not enough rep

how to delay the return in React until fetch is done

I am using React js and want to render data that I got it from API using Fetch .
the problem is I can't display the fetch results because the Return scope of React excute befor the fetch method done!!
Please any help of how I can solve this ???
this is a part of the function that do the Fetch :
initFourSquare = () => {
return fetch(FourSquareAPI)
.then(data => {
return data.json()
})
.catch((error) => {
console.log(error.message)
})
};
this is the render part of react where I called the function (initFourSquare)
render() {
var info = []
this.initFourSquare().then(response => {
info = response.response.venues
console.log(info) //the result is appear here
})
setTimeout(function(){ console.log(info[0].name) }, 1000);//the result is appear here
return (
<div>
<h1>{info}</h1> // it display nothing !!!
</div>
);
}
}
Any API/asynchronous call shouldn't be made in render function instead, you should do that in componentDidMount function if it is to be done once which seems to be your case and set the response in state which you can use in render. Make sure that you either initialise the state correctly or provide a conditional check for existence before using state variable
class App extends React.Component {
state = {
info: []
}
componentDidMount() {
var info = []
this.initFourSquare().then(response => {
info = response.response.venues
this.setState({info})
})
}
initFourSquare = () => {
return fetch(FourSquareAPI)
.then(data => {
return data.json()
})
.catch((error) => {
console.log(error.message)
})
};
render() {
const { info } = this.state;
return (
<div>
<h1>{info}</h1>
</div>
);
}
}

How to remount the data after the textbox has been emptied -- REACT

I'm new to react. I have a component here that will automatically load the data from a database (mongo) via express server url when the page was loaded.
componentDidMount() {
var self = this;
fetch('/movies')
.then((resp) => resp.json())
.then(movies => self.setState({
movies: movies
}))
.catch((err) => console.log(err));
}
this will load all data and display them on screen and when the user types in to the textbox it will update the display to the search term:
movieSearch(term){
var self = this;
axios.get('/moviesbytitle', {
params: {
term: term
}
})
.then(movies => self.setState({
movies: movies.data
}))
.then(() => console.log('this is is a state >>>>+++', self.state))
.catch((err) => console.log(err));
}
render(){
const movieSearch = _.debounce((term) => { this.movieSearch(term) }, 300);
return (
<div>
<MovieTopBar />
<SearchBar onSearchTermChange={movieSearch}/>
<MovieList movies={ this.state.movies }/>
<MovieFooter />
</div>
);
}
};
class SearchBar extends Component{
constructor(props){
super(props);
this.state = { term: '' };
this.onInputChange = this.onInputChange.bind(this);
}
<input type="text" value={this.state.term} onChange={event => this.onInputChange(event.target.value)} className="form-control" id="" placeholder="Enter the title of the movie..." />
onInputChange(term){
this.setState({ term });
this.props.onSearchTermChange(term);
}
}
I am trying to find a way how can I update the data searched -> to the data when it was first mounted w/c displays the whole data whenever the textbox for search is emptied. Is there a specific lifecycle method to do that? or do I need to do a separate function? Thanks for the help in advance!
I would suggest
this.movieSearch = _.debounce((term) => { this.movieSearch(term) }, 300);
move it to constructor
so you can avoid creating a new function each render, this will be expensive on memory
you also do not need var self = this;
using anonymous functions like () => {} will preserve this
and coming back to question, if I understood it, you want to show original movie list when user deletes input in search,
componentDidMount() {
var self = this;
fetch('/movies')
.then((resp) => resp.json())
.then(movies => {
self.setState({ movies: movies });
this.originalResults = movies;
})).catch((err) => console.log(err));
}
you can do it by keeping reference to original results
and then you can add
movieSearch(term){
if (term === '') {
this.setState({movies:this.originalResults})
return; // exit the search function
} ...rest of function
As #AlexGvozden said, you should move your movieSearch to be part of the component class, so
class ShowMoviesList {
constructor() {
this.movieSearch = this.movieSearch.bind(this);
}
componentDidMount() {
this.getAllMovies();
}
getAllMovies() {
fetch('/movies')
.then((resp) => resp.json())
.then(movies => this.setState({
movies: movies
}))
.catch((err) => console.log(err));
}
getMovieByTitle(term) {
axios.get('/moviesbytitle', {
params: {
term: term
}
})
.then(movies => this.setState({
movies: movies.data
}))
.then(() => console.log('this is is a state >>>>+++', this.state))
.catch((err) => console.log(err));
}
movieSearch(term) {
// remember prevents blank spaces
const currentTerm = term ? term.trim() : term;
// remember clear last getMovieByTitle call
if (this.timer) {
clearTimeout(this.timer);
}
// if term is empty, so call getAllMovies again
// prevent call cache movies, because cache movie will always return the first call
// but won't render new movies if exists
if (!currentTerm) {
return this.getAllMovies();
}
this.timer = setTimeout(() => { this.getMovieByTitle(currentTerm); }, 300);
}
render() {
return (
<div>
<MovieTopBar />
<SearchBar onSearchTermChange={this.movieSearch}/>
<MovieList movies={ this.state.movies }/>
<MovieFooter />
</div>
)
}
}
if better call getAllMoviesfunction again when termis empty, because you will show the real current movies instead a cache movies version from first call.
I hope it can help you.

Categories

Resources