Fetching methods depends on each other - reactjs

I have been trying to fetch data from two sources in componentDidMount and second component's url relies on the data fetched from the first component, but it looks that state is not "updated" inside ComponenDidMount. I have tried to resolve it by using fetchDuel() in the constructor with no luck. Any suggestions? Thanks in advance!
class DuelDetail extends React.Component {
state = {
duel: [],
dataset: null
};
fetchDuel = () => {
const duelID = this.props.match.params.duelID;
axios.get(`http://127.0.0.1:8000/api/duel/${duelID}`,
{'headers': {'Authorization': `Token ${localStorage.getItem('token')}`}})
.then(res => {
this.setState({
duel: res.data
});
});
};
fetchDataset = () => {
axios.get(`http://127.0.0.1:8000/api/dataset/${this.state.duel.dataset}`,
{'headers': {'Authorization': `Token ${localStorage.getItem('token')}`}})
.then(res => {
this.setState({
dataset: res.data
});
});
};
componentDidMount() {
this.fetchDuel()
this.fetchDataset()
}

Just call the second function in the then() block of the first and pass the data as a param. setState is asynchronous so you can't rely on the data to be set immediately.
fetchDuel = () => {
const duelID = this.props.match.params.duelID;
axios.get(`http://127.0.0.1:8000/api/duel/${duelID}`,
{'headers': {'Authorization': `Token ${localStorage.getItem('token')}`}})
.then(res => {
this.setState({
duel: res.data
});
this.fetchDataset(res.data);
});
};

As the 2 actions are async you need to handle it accordingly.
Axios get returns a promise .So you can call the second action in the then block of the first action.
Also, setState is an aync action.(It gets queued up and doesn't get triggered instantly).
Use the data received from the first action, in its then block, pass it to the second action

Just call the second function in the .then() of the first function using data from the response. Example:
class DuelDetail extends React.Component {
state = {
duel: [],
dataset: null
};
fetchDuel = () => {
const duelID = this.props.match.params.duelID;
axios.get(`http://127.0.0.1:8000/api/duel/${duelID}`,
{'headers': {'Authorization': `Token ${localStorage.getItem('token')}`}})
.then(res => {
this.setState({
duel: res.data
});
this.fetchDataset(res.data.dataset)
// pass whatever property you get from the response here.
});
};
fetchDataset = (datasetId) => {
axios.get(`http://127.0.0.1:8000/api/dataset/${datasetId}`,
{'headers': {'Authorization': `Token ${localStorage.getItem('token')}`}})
.then(res => {
this.setState({
dataset: res.data
});
});
};
componentDidMount() {
this.fetchDuel()
}

Related

This.props doesn't load data from react/redux

I have the following method:
const getAgentData = () => {
axios.get('http://localhost:3000/api/agent', {
headers: {
'Content-Type': 'application/json'
}
})
.then(async res => {
const dbData = res.data.data;
let dataForTable = dbData.map( el => {
let obj = {};
obj._id = el._id;
obj.name = el.name;
obj.phone = el.phone;
if(el.name) {obj.email = el.email}
return obj;
})
await dispatch({ type: 'ADD_PHOTOGRAPHERS', dataForTable });
})
.then(() => {
setLoading(false)
})
.catch((error) => {
console.error(error)
})
}
I update the redux state with this line: await dispatch({ type: 'ADD_PHOTOGRAPHERS', dataForTable });
in a child component I run this:
componentDidMount() {
console.log(this.props.photographers)
}
In the original state this.props.photographers = [];
this is what is logged to the console, it never logs the udpated redux state. From my understading redux should automatically update and it should console.log the udpated state. Any idea what I'm doing wrong?
I also tried logging data with the props being here but it's also an empty array:
class DataTableComponent extends Component {
constructor(props) {
super(props)
this.state = {
data: this.props.photographers,
loading: false,
name: '',
phone: '',
email: '',
}
}
...
My redux map to props in the child component I'm describing is:
function mapStateToProps(state) {
return {
photographers: state.Customizer.photographers
}
}
export default connect(mapStateToProps)(DataTableComponent);
Check if await dispatch({ type: 'ADD_PHOTOGRAPHERS', dataForTable });, seems that you are not sending the payload correctly.
You can log or debug how this action payload data is coming to its reducer.

How to save single object into state, from json end point axios

UPDATE:
axios
.get("https://cors-anywhere.herokuapp.com/" + "https://api.linkedin.com/v2/me", config)
.then(response => {
this.setState({profile: response.data})
})
^ saved the object in state for me :) Thanks everyone!!
I am a newbie to react. I am trying to save a single object from a JSON end point into the state of my react component. I am definitely returning the JSON data in the response. However it is not being saved into the state, can you see where I am going wrong?
// State needed for the component
constructor(props) {
super(props);
this.state = {
profile: {},
};
}
// Grabs profile data from the json url
private getProfile() {
let config = {
headers: {'Authorization':'Bearer AQVVEqNXTWV....'}
}
axios
.get("https://cors-anywhere.herokuapp.com/" + "https://api.linkedin.com/v2/me", config)
.then(response =>
response.data(profile => ({
id: `${ profile.id }`
}))
)
.then(profile => {
this.setState({
profile
});
})
// We can still use the `.catch()` method since axios is promise-based
.catch(error => this.setState({ error, isLoading: false }));
}
JOSN data returned:
{
"localizedLastName": "King",
"id": "fm0B3D6y3I",
"localizedFirstName": "Benn"
}
Your first then block looks wrong.
Try to do console.log there like this:
.then(response => {
console.log(response); // I am sure that you will get profile inside response.data or something similar
return response.data(profile => ({
id: `${ profile.id }`
}));
})
If you want to keep your first then that "prepares the data", then you should return a promise instead of data, like:
let config = {
headers: {'Authorization':'Bearer AQVVEqNXTWV....'}
}
axios
.get("https://cors-anywhere.herokuapp.com/" + "https://api.linkedin.com/v2/me", config)
.then(response => {
return new Promise((resolve, reject) => {
resolve( {
id: `${ response.data.id }`
});
});
}
)
.then(profile => {
this.setState({
profile
});
})
// We can still use the `.catch()` method since axios is promise-based
.catch(error => this.setState({ error, isLoading: false }));
Here's an example of how that would work:
I do believe that's a bit of an overkill though and you should be able to just set your state in the first then such as:
this.setState({profile: {id : response.data.id}});
Try to remove the second then, like this:
axios
.get("https://cors-anywhere.herokuapp.com/" + "https://api.linkedin.com/v2/me", config)
.then(response => {this.setState({ profile: response.data })};
})
}))

Updating object in react under componentDidMount

I able to get the right data from my API node.js server. however when i try to setstate the object to render it it keeps returning null
i tried to use spread operator before the response but it still not working
import React, { Component } from "react";
import axios from "axios";
class Profile extends Component {
constructor(props) {
super(props);
this.state = {
UserData: null,
isLoading: false,
error: null
};
}
componentDidMount() {
this.setState({ isLoading: true });
axios
.get(
`http://localhost:5000/api/v1/profile/${this.props.match.params.platform}/${this.props.match.params.gamertag}`
)
.then(response => {
console.log(response.data);
})
.then(response => {
this.setState({
UserData: response.data,
isLoading: false
});
})
.catch(error => this.setState({ error, isLoading: false }));
}
render() {
const { isLoading, UserData } = this.state;
if (isLoading) {
return <p>Loading ...</p>;
}
console.log(UserData);
return <div>{UserData}</div>;
}
}
export default Profile;
when i try to log. the UserData log "null", but the "console.log(response.data)" works fine so it have to do something with the setState
when you chain data method like .then(), the following chained methods automatically receive value returned by the previous function.
getData
.then(res => console.log(res))
console.log itself will return nothing, thus the following .then() method will receive nothing.
getData
.then(res => console.log(res))
.then(data => console.log(data))
So if you do this, the second console.log() will log null.
You can fix it by returning something in your console.log step:
getData
.then(data => {
console.log(data);
return data;
})
.then(data => this.setState({ data: data }));
And the second console.log() will log properly.
You don't need two chain two then()'s, you can get the response and set the state after .then()
componentDidMount() {
this.setState({ isLoading: true });
axios
.get(
`http://localhost:5000/api/v1/profile/${this.props.match.params.platform}/${this.props.match.params.gamertag}`
)
.then(response => {
this.setState({
UserData: response.data,
isLoading: false
});
})
.catch(error => this.setState({ error, isLoading: false }));
}

How do i set the state from one API call and use the data for URL in next API call?

I need the data from API call 1 to add to the URL of API call 2. The data from API 2 will go into the URL for API 3. I am setting the state on each Axios request and it is not working. Returning undefined
componentDidMount() {
// Get the IP adress of user
axios
.get('https://api.ipify.org?format=json')
.then(res => {
this.setState({
ip: res.data.ip
});
console.log(`IP : ${this.state.ip}`);
})
.catch(err => console.log(err));
// GET the coordinates of a location based on IP adress
axios
.get(
'https://geo.ipify.org/api/v1?apiKey=YOUR_API_KEY&ipAddress=24.8.227.87'
)
.then(res => {
this.setState({
latitude: res.data.location.lat,
longitude: res.data.location.lng
});
console.log(
`Latitude: ${this.state.latitude}. Longitude: ${this.state.longitude}`
);
})
.catch(err => console.log(err));
// Make the API call on page load
axios({
method: 'get',
url: `https://developers.zomato.com/api/v2.1/geocode?lat=39.6924553&lon=-105.0256318`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
'user-key': 'USER_KEY'
}
})
.then(res => {
const restaurantsNearMe = res.data.nearby_restaurants;
this.setState({
restaurants: restaurantsNearMe
});
// Pick out a random retaurant from what the API returns
var randomRestaurant =
restaurantsNearMe[
Math.floor(Math.random() * restaurantsNearMe.length)
];
// Select only the data that you want
var finalResult = {
name: randomRestaurant.restaurant.name,
id: randomRestaurant.restaurant.id,
rating: randomRestaurant.restaurant.user_rating.aggregate_rating,
ratingColor: randomRestaurant.restaurant.user_rating.rating_color,
address: randomRestaurant.restaurant.location.address,
delivery: randomRestaurant.restaurant.is_delivering_now,
typeOfFood: randomRestaurant.restaurant.cuisines
};
this.setState({
restaurant: finalResult
});
console.log(this.state.restaurant);
})
.catch(err => console.log(err));
}
You need a callback in setState, and in that callback you need to call your second API and so on. Check this.
This is what you want,
axios
.get('https://api.ipify.org?format=json')
.then(res => {
this.setState({
ip: res.data.ip
}, () => {
// GET the coordinates of a location based on IP adress
axios
.get(
'https://geo.ipify.org/api/v1?apiKey=YOUR_API_KEY&ipAddress=24.8.227.87'
)
.then(res => {
this.setState({
latitude: res.data.location.lat,
longitude: res.data.location.lng
}, () => {
// Make the API call on page load
axios({
method: 'get',
url: `https://developers.zomato.com/api/v2.1/geocode?lat=39.6924553&lon=-105.0256318`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
'user-key': 'USER_KEY'
}
})
.then(res => {
const restaurantsNearMe = res.data.nearby_restaurants;
this.setState({
restaurants: restaurantsNearMe
});
// Pick out a random retaurant from what the API returns
var randomRestaurant =
restaurantsNearMe[
Math.floor(Math.random() * restaurantsNearMe.length)
];
// Select only the data that you want
var finalResult = {
name: randomRestaurant.restaurant.name,
id: randomRestaurant.restaurant.id,
rating: randomRestaurant.restaurant.user_rating.aggregate_rating,
ratingColor: randomRestaurant.restaurant.user_rating.rating_color,
address: randomRestaurant.restaurant.location.address,
delivery: randomRestaurant.restaurant.is_delivering_now,
typeOfFood: randomRestaurant.restaurant.cuisines
};
this.setState({
restaurant: finalResult
});
console.log(this.state.restaurant);
})
.catch(err => console.log(err));
});
console.log(
`Latitude: ${this.state.latitude}. Longitude: ${this.state.longitude}`
);
})
.catch(err => console.log(err));
});
console.log(`IP : ${this.state.ip}`);
})
.catch(err => console.log(err));
one thing to keep in mind, this.setState is not synchronous. React batches multiple set state calls to improve render performance. That's why you might be seeing undefined in the console.log.
setState method takes a callback as second parameter.
this.setState(newState, callbalck)
so try to console log in the callback and give it a try.
I dont know how do you call API's but, try something like this:
In componentDidMount you can do this:
async componentDidMount(){
const resApiOne = await callFirstApi();
this.setState({resApiOne});
const resApiTwo = await callSecondApi(resApiOne);
this.setState({resApiTwo});
}

componentDidMount not sending data after setting state

I am decoding a token to get the current users email address and setting to facultyEmail state and sending that to the backend to get a response. But facultyEmail is empty because componentDidMount is asynchronous ,it works outside the componentDidMount() but I don't know any way to handle the axios get request with params outside the componentDidMount i dont have event to invoke it.Thanks for the help
componentDidMount() {
const token = localStorage.usertoken;
const decoded = jwt_decode(token);
this.setState({
facultyEmail: decoded.email
});
axios
.get("faculty/Course", {
params: {
facultyEmail: this.state.facultyEmail
}
})
.then(res => {
this.setState({
class: res.data
});
})
.catch(err => {
console.log(err);
});
console.log("courses", this.state.facultyEmail);
}
The setState is asynchronous. You have to use setState callback or async/await
using callback
componentDidMount() {
const token = localStorage.usertoken;
const decoded = jwt_decode(token);
this.setState({
facultyEmail: decoded.email
}, () => {
axios
.get("faculty/Course", {
params: {
facultyEmail: this.state.facultyEmail
}
})
.then(res => {
this.setState({
class: res.data
});
})
.catch(err => {
console.log(err);
});
console.log("courses", this.state.facultyEmail);
});
}
using async/await
async componentDidMount() {
try {
const token = localStorage.usertoken;
const decoded = jwt_decode(token);
await this.setState({
facultyEmail: decoded.email
});
const res = await axios.get("faculty/Course", {
params: {
facultyEmail: this.state.facultyEmail
}
})
this.setState({
class: res.data
});
console.log("courses", this.state.facultyEmail);
} catch (err) {
console.log(err);
}
}
You are using same email you are using in setState to make the API call, there is no need for two setStates. That would cause us anomalies and is not a recommended practice. You can do this in two ways:
Way 1:
componentDidMount() {
const token = localStorage.usertoken;
const decoded = jwt_decode(token);
axios.get("faculty/Course", {
params: {
facultyEmail: decoded.email
}
}).then(res => {
this.setState({
class: res.data,
facultyEmail: decoded.email
});
}).catch(err => {
console.log(err);
});
}
render() {
console.log(this.state.class, this.state.facultyEmail);
// This will have the values from setstate triggered inside axios.
return(
<div> Sample </div>
)
}
Alternate approach:
loadDataFromApi(email) {
axios.get("faculty/Course", {
params: {
facultyEmail: email
}
}).then(res => {
this.setState({
class: res.data
});
}).catch(err => {
console.log(err);
});
}
componentDidMount() {
const token = localStorage.usertoken;
const decoded = jwt_decode(token);
this.setStats({
facultyEmail: decoded.email
}, () => {
// The callback function would reflect the updated email.
this.loadDataFromApi(this.state.facultyEmail);
});
}
Why not just store facultyEmail in memory until the 2nd setState, avoiding the first one? The axios call is async, so you'll need to put the console.log in the render function (and you should only log it once it's actually in state).
componentDidMount() {
const token = localStorage.usertoken;
const decoded = jwt_decode(token);
const facultyEmail = decoded.email;
axios
.get("faculty/Course", { params: { facultyEmail } })
.then(res => { this.setState({ class: res.data, facultyEmail }); })
.catch(err => { console.log(err); });
}
render() {
if (this.state.facultyEmail) console.log("courses", this.state.facultyEmail);
return ();
}

Resources