How to set state of response from axios in react - javascript

How do I set the state of a get response in axios?
axios.get(response){
this.setState({events: response.data})
}

You have a syntax error here. You should try this instead
var self = this;
axios.get('/url')
.then(function (response) {
console.log(response);
self.setState({events: response.data})
})
.catch(function (error) {
console.log(error);
});
//the rest of the code
var a = 'i might be executed before the server responds'
There are a few things to note here:
axios.get is an asynchronous function which means that the rest of the code will be executed .And when the response of the server arrives, the function passed to then will be executed. The return value of axios.get('url') is called a promise object. You can read more about it here
this keyword has a different value depending of where it is called. this in this.setState should refer to the constructor object, and when you call this inside a function, it refers to the window object. That is why i assigned this to the variable self. You can read more about this here
Pro tip:
If you use ES6, you would want to use arrow functions (which don't have their own this) and use this.setState without assigning this to a variable. more about it here
axios.get('/url')
.then((response) => {
console.log(response);
this.setState({events: response.data})
})
.catch((error)=>{
console.log(error);
});
Here is a complete example https://codesandbox.io/s/rm4pyq9m0o containing best practices commonly used to fetch data including error handling, try again and loading. This provides a better User experience. You are encouraged to modify the code and play around to get more insights about it.

This isn't working because "this" is different inside of axios. "this" inside axios refers to the axios object, not your react component. You can resolve this with .bind
Also axios isnt being used properly.
it should look something like
axios.get("/yourURL").then(function(response) {
this.setState({ events: response.data });
}.bind(this));
Alternatively if using es6 you could sub out the function for an arrow function and get the same effect without bind
axios.get("/yourURL").then(response => {
this.setState({ events: response.data });
});

Simply try this node js
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then(res => {
const persons = res.data;
this.setState({ persons });
})
if you are using react js then you first import in component than use axios
like this:
import React from 'react';
import axios from 'axios';
export default class PersonList extends React.Component {
state = {
persons: []
}
componentDidMount() {
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then(res => {
const persons = res.data;
this.setState({ persons });
})
}
render() {
return (
<ul>
{ this.state.persons.map(person => <li>{person.name}</li>)}
</ul>
)
}
}

I have dealt with promises similar to that in the past when I was learning react. What I did was put the api call on the componentDidMount method and set the state to an initial value. I used a loader while the data was being fetched.
componentDidMount() {
const self = this;
axios.get(response){
self.setState({ events: response.data });
}
As of now, I would use something similar to what checkenrode said.

Do something like this:
var self= this; // self will now be referred to your component
axios.get("http://localhost:3001/get_user?id=" + id)
.then(function (response) {
if(response.data.rows != null)
user_detail = response.data.rows;
console.log(response);
self.setState({email: user_detail.name, name: user_detail.name})
})

Related

How can I set state in a function that is in a utility file?

I had a function in a react class that I was using to pull data.
It was working great, but I then found out I needed to use the same function in a few other places.
So I decided to put the function in it's own file so I could re-use it.
So here it is:
import axios from 'axios';
export const getGalaxyName = async (id) => {
try {
const { data: response } = await axios.get(`/api/scienceClass/galaxy/${id}`)
this.setState({ galaxyName: response.name });
}
catch (error) {
console.log(error);
}
}
And then in a component, I use it like this:
import { getGalaxyName } from './ScienceClassUtils';
render() {
getGalaxyName(slide.z_GalaxyId);
But now I am getting this error:
ScienceClassUtils.js:35 TypeError: Cannot read property 'setState' of
undefined
I am guessing it's because I'm trying to still set the state in the function like I did when it was originally inside the react class.
So how can I still use it now that it's separated out in another file, but still have this.setState({ galaxyName: response.name }); ?
Thanks!
Easiest way from here to there might be to have it return the promise instead of calling setState directly:
const { data } = await axios.get(`/api/scienceClass/galaxy/${id}`)
return data.name;
Then your component or anyone else could do whatever they need to do with it:
getGalaxyName(id).then(galaxyName => this.setState({ galaxyName });
There are at least two possible approaches:
Create a helper method for setting the galaxyName and then pass it to axios utility, and it will call it and pass the response.name to it.
Return the response from axios utility and use Promise methods .then, .catch, .finally, to handle the success, fail, finished cases as needed. Note that, the data you return from the axios utility will be passed to these methods as parameter.
The bottom line is you can't set the state outside a React component. Make your utility function return the data you need and set the state from within your react component.
Refactor your util function to look like this
import axios from 'axios';
export const getGalaxyName = async (id) => {
try {
const { data: response } = await axios.get(`/api/scienceClass/galaxy/${id}`)
return { galaxyName: response.name }
} catch (error) {
console.log(error);
}
}
From within your component call the method in a componentDidMount. lifecycle hook like this
class SampleComponent extends React.component {
// Add this lifecycle hook
componentDidMount() {
getGalaxyName(slide.z_GalaxyId).then((data) => {
this.setState(data)
});
}
render() {
// use state data as needed here
}
}

Error when using a function that requests API data

I'm working on this app that takes data from a movies API and I want to work with it.
I have this function that gets the API data:
/** #format */
const fetchMovie = movie => {
var APIKEY = "xxxxxxxxxxxxxxxxxxxxxx";
var API2 =
"https://api.themoviedb.org/3/search/movie?api_key=xxxxxxxxxx&language=en-US&page=1&include_adult=false&query=avengers";
var API = `https://api.themoviedb.org/3/search/movie?api_key=${APIKEY}&language=en-US&page=1&query=${movie}`;
fetch(API2)
.then(data => data.json())
.then(movies => console.log(movies) || movies.items)
.catch(error => {
console.log(error);
return null;
});
};
export default fetchMovie;
And I have this App class that uses the API data:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
activeMovie: "Avengers",
loading: true,
allMovies: []
};
}
componentDidMount() {
this.getData(this.activeMovie);
}
componentDidUpdate(prevState) {
if (prevState.activeMovie !== this.state.activeMovie) {
this.getData(this.state.activeMovie);
}
}
getData(movie) {
this.setState({
loading: true
});
fetchMovie(movie).then(data => {
this.setState({
allMovies: data,
loading: false
});
});
}
Now, before this I have used the same methodology and it worked but I don't know why the I get
TypeError: Object(...)(...) is undefined // this line fetchMovie(movie).then(data => {
The API is good, I can console log the data before it gets to the App component, but the function in the app component somehow doesn't work. any clues?
That's simply because your function fetchMovie() doesn't return a Promise so that you than use .then() after it. You can return a promise instead. However the logic in your code is probably a bit shaky. You might as well look that up because it goes into an infinite loop, consider debugging component life cycles for that.
To return a promise from your function, you can use a similar approach as I wrote in here: https://codesandbox.io/s/small-sun-sfcyv.
You are not returning any promise from your fetchMovie function, that way you can't use the .then so right now you only have access to that data in your fetchMovie. A possible solution would be defining your function as async and then you would be able to return your data from that function.
Try this.
/** #format */
const fetchMovie = movie => {
var APIKEY = "xxxxxxxxxxxxxxxxxxxxxx";
var API2 =
"https://api.themoviedb.org/3/search/movie?api_key=xxxxxxxxxx&language=en-US&page=1&include_adult=false&query=avengers";
var API = `https://api.themoviedb.org/3/search/movie?api_key=${APIKEY}&language=en-US&page=1&query=${movie}`;
return fetch(API2)
.then(data => data.json())
.then(movies => console.log(movies) || movies.items)
.catch(error => {
console.log(error);
return null;
});
};
export default fetchMovie;

Why can't I set server's answer to state?

I'm trying to get JSON from api.openweathermap.org and set it to state, but as result I get console.log
What should I do set JSON's info to state.weather?
import React, { Component } from 'react';
class GetWeather extends Component {
constructor(props) {
super(props);
this.state = {
weather: {},
temp: ''
}
};
weather = async (e) => {
e.preventDefault();
try {
let response = await fetch('http://api.openweathermap.org/data/2.5/weather?q=London,uk&APPID=b40640de9322c8facb1fcb9830e8b1f4');
let data = await response.json();
// if I will use response.text() here, than next console.log will show me the object literal that I got from server
console.log('data: ' + data);
await this.setState({weather: data});
console.log('state ' + this)
} catch (e) {
console.log(e);
}
}
render() {
return (
<button onClick={this.weather} />
)
}
}
export default GetWeather;
React state updates are asynchronous and occur at the end of the function call (i.e. all set state calls are "collated" and processed together, part of the reconciliation), so console logging the state update immediately after doesn't work.
Try to use the setState callback
this.setState({weather: data}, () => console.log('state', this.state));
State will update and call the callback afterwards, synchronously, so you'll see the new state value. You also do not need to await it.
You cannot await setState. To execute code after your state has changed, setState actually has 2nd argument which is a callback function that is executed after the state has changed. Your code should look something like this:
console.log(data);
this.setState({weather: data}, () => {console.log(this.state)});
Here you can see another problem. Since you are concatenating a string ('data:') with an object, your object is converted to the string and you get [object Object]. To avoid this, print either only the object or print an object separately from the string like this: console.log('data:', data). Note that I used a comma here, not a plus.

Asynchronous ActionCreator in React Redux

I'm pretty new in React-Redux. Was working on an application. The thing is that I faced some issues with asynchronous execution of Redux actionCreator, may be.
Below is my component. Say, I want to call an actionCreator from componentDidMount() or from an onclick event listener.
class Dashboard extends PureComponent {
componentDidMount() {
this.props.getProductsAndPackages();
let something = [];
something = this.props.products;
}
....................................
}
Or , the function this.props.getProductsAndPackages(); can be an onClick event handler that does the same thing, context is the same. I'll ask my question after first explaining my code.
At the lower side of my Dashboard container:
Dashboard.propTypes = {
getProductsAndPackages: PropTypes.func.isRequired,
products: PropTypes.array.isRequired,
.......................
};
const mapStateToProps = (state) => {
return {
.....................
products: state.products.products,
...................
};
};
const mapDispatchToProps = (dispatch) => {
return {
getProductsAndPackages: () => dispatch(getProductsAndPackagesActionCreator()),
};
};
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Dashboard));
My actionCreator goes like:
export const getProductsAndPackagesActionCreator = () => {
return (dispatch) => {
dispatch(productsIsLoading(true));
let url = 'xyz';
if(!!localStorage.getItem('_token')) {
const local_token = localStorage.getItem('_token');
const fullToken = 'Bearer '.concat(local_token);
axios.get(url, {headers: {Authorization: fullToken}})
.then(response => {
dispatch(productsIsLoading(false));
if (response.data.statusCode === 200) {
dispatch(productsFetched(true));
dispatch(products(response.data.data));
} else {
dispatch(productsFetched(false));
dispatch(productsErrors(response.data.message));
}
})
.catch(error => {
});
} else {
axios.get(url)
.then(response => {
dispatch(productsIsLoading(false));
if (response.data.statusCode === 200) {
dispatch(productsFetched(true));
dispatch(products(response.data.data));
} else {
dispatch(productsFetched(false));
dispatch(productsErrors(response.data.message));
}
})
.catch(error => {
console.log(error);
dispatch(productsIsLoading(false));
dispatch(productsErrors(error.message));
});
}
};
};
Now, I want my getProductsAndPackagesActionCreator() to return a Promise or anything that would allow my something variable to get the actual data returned from the server. Right now, by the time I'm getting actual data, the line something=this.props.products has already been executed and I get back the initialValue that was set for products.
I know, whenever I'll receive the populated products, component will re-render, but that does not help my decision making.
I'm using redux-thunk, by the way.
What should I do now ? Sorry for such a long post.
Actually I wanted getProductsAndPackagesActionCreator() to return a promise, which was pretty straightforward, to be honest. I figured out that if you just return the axios.get() or axios.post(), it will return a promise. So, the modified code looked like below:
export const getProductsAndPackagesActionCreator = () => {
return (dispatch) => {
dispatch(productsIsLoading(true));
let url = 'xyz';
if(!!localStorage.getItem('_token')) {
return axios.get(url, {headers: {Authorization: fullToken}})
.then(response => {
............
............
})
.catch(error => {
});
} else {
return axios.get(url)
.then(response => {
...........
...........
})
.catch(error => {
console.log(error);
});
}
};
};
And then, I could do something like below in componentDidMount() or on any onClick event:
this.props.getProductsAndPackages().then(() => {
this.setState({
...this.state,
clicked_product: this.props.product_by_id
}, () => {
//do other stuffs
});
});
Feel free to let me know if there's any issue.
I think you are close to getting what you want. First of all, you should understand that redux actions and react actions like setState are asynchronous, so you have to apply your logic keeping this in mind. I'm going to explain what i think in some points:
You have called the action creator in the correct place componentDidMount, also you can call this action in any onClick if you want.
As soon as you dispatch the action you are changing your redux state setting loading true I suppose. So now you can access this property in your render function, so you can render a Loader until your api call finishes.
When your ajax function finishes, with an error or not, I suppose you are setting loading to false and updating your products data, so you can render now your loaded products in your dashboard.
Are you sure that you have to compare your empty products array with the received data? Maybe you can check in your render function if (!this.props.products.length) return null, when you load your page you will see a loader function and later your dashboard with the products.
If you really need to compare previous products with received products componentDidUpdate is your method. In this method, you can access your previous props and compare with actual props, be careful comparing arrays, remember [] === [] is false. Maybe you can compare the length, something like
componentDidUpdate(prevProps){
if(prevProps.products.length !=== this.props.products.lenth){
doSomething()
}
}
Just to say that componentDidUpdate is executed after render, so be careful with your code to no-execute extra renderings.
Hope it helps, if you dont understand anyting just tell me :)

ReactJS: How to rerender component after fetching data

So I have a component which displays some data fetched from an external API and saved to localStorage. I have put a fetchData() function that does the job, and I call this function from within componentWillMount(). It looks like this:
componentWillMount() {
this.fetchData();
}
...
fetchData() {
if(localStorage.myData === undefined) {
fetch(apiUrl,{
method: 'GET',
headers: ...
})
.then(function(data) {
localStorage.setItem('myData', data);
}).catch(function(error) {
// error handling
})
} else { return true; }
}
The idea here is to check on every render if the data is set in localStorage, otherwise fetch it, save data and then rerender. However, I can't get it to rerender after the data is stored in localStorage. I have tried using this.setState({data: data}) instead in the fetchData function, but this is undefined.
What am I doing wrong here? Thank's in advance.
this in your class has a different context than the this inside of your then() function. For your particular case, you can do the following
fetch(apiUrl,{
method: 'GET',
headers: ...
})
.then(function(data) {
// This now refers to your component
this.setState({data: data});
}.bind(this))
.catch(function(error) {
// error handling
})
Or as Utro suggested, you can use arrow functions that will not create their own context thus allowing you to use this appropriately.
fetch(apiUrl,{
method: 'GET',
headers: ...
})
.then(data => {
// This now refers to your component
this.setState({data: data});
}).catch(function(error) {
// error handling
})
This object inside .then will refer to object of Promise call not the object of Component.
vat that = this;
declare some variable of this to that and then you can use
that.setState({})

Categories

Resources