For my project, I have to get several APIs in my projects, these APIs are linked to the others, i.e. they have the same data ...
Here is my code
export default class App extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
Promise.all([
getData(),
getData('?page=2'),
])
.then(([dataSource1, dataSource2]) => {
this.setState({
isLoading: false,
isLoading2: false,
dataSource1,
dataSource2,
});
})
.catch((error) => {
// handle errors
});
}
render() {
const getData = (subpath = '') => fetch(`https://api.rawg.io/api/games${subpath}`)
.then(res => res.json())
.then(result => result.results);
console.log(getData)
}
I tried with axios but without success ...
When I remove the comment, it shows me only the second fetch ...
You need two separate fetch calls for each API. To wait for both to finish, use Promise.all.
const getData = (subpath = '') => fetch(`https://api.rawg.io/api/games${subpath}`)
.then(res => res.json())
.then(result => result.results);
componentDidMount() {
Promise.all([
getData(),
getData('?page=2'),
])
.then(([dataSource1, dataSource2]) => {
this.setState({
isLoading: false,
isLoading2: false,
dataSource1,
dataSource2,
});
})
.catch((error) => {
// handle errors
});
}
Related
export const FETCH_DB_BEGIN = 'FETCH_DB_BEGIN'
export const FETCH_DB_SUCCESS = 'FETCH_DB_SUCCESS'
export const FETCH_DB_FAILURE = 'FETCH_DB_FAILURE'
export const fetchDatabase = () => {
return dispatch => {
const profile_url = 'localhost:5000/profiles'
const release_url = 'localhost:5000/releases'
const emp_url = 'localhost:5000/users'
let promises = []
let options = {
headers: header,
method: 'get',
mode: 'cors',
body: null,
}
dispatch(fetchDbBegin());
// run the script async. change state when it's done.
let profile_promise = new Promise((resolve, reject) => {
fetch(profile_url, options)
.then(res => res.json())
.then(resText => {
// Use Dispatch Here?
})
}).catch(err => {
console.log(err)
})
promises.push(profile_promise)
// run the script async. change state when it's done.
let release_promise = new Promise((resolve, reject) => {
fetch(release_url, options)
.then(res => res.json())
.then(resText => {
})
}).catch(err => {
console.log(err)
})
promises.push(release_promise)
// run the script async. change state when it's done.
let emp_promise = new Promise((resolve, reject) => {
fetch(emp_url, options)
.then(res => res.json())
.then(resText => {
})
}).catch(err => {
console.log(err)
})
promises.push(emp_promise)
Promise.all(promises).then(values => {
console.log(values)
})
}
}
export const fetchDbBegin = () => ({
type: FETCH_DB_BEGIN
});
export const fetchDbSuccess = (data) => ({
type: FETCH_DB_SUCCESS,
payload: { data }
});
export const fetchDbFailure = (err) => ({
type: FETCH_DB_FAILURE,
payload: { err }
});
I am in a process of refactoring a React class component to use Redux. It initially had all API calls inside the componentDidMount and it was so messy.
I am using redux-thunk to move this out from the class component.
The fetchDatabase in my databaseAction.js does everything that componentDidMount did in the class component.
Normally if it was a single API call, I would have just dispatched the fetchDbSuccess as the API call was done successfully. However, using Promise.All which takes three async API calls, I am not sure whether I should
create a separate action for each API call (fetchProfileSuccess, fetchReleaseSuccess, and fetchUserSuccess) and dispatch each one of them at the end of each Promise (the place where I put //Use Dispatch Here? in the code.
OR
Just dispatch single fetchDbSuccess when the Promise.all gets resolved.
If I choose to do 2, am I supposed to update all three states in my reducer?
Thanks
You should only dispatch and update state if you have code that cares about said state updates. For example, if you're just wanting to show a single spinner then have the spinner go away when fully completed, your user doesn't necessarily care about each atomic operation, so you don't need it reflected in state. If you have a UI that does show each, then you would want those extra dispatches.
By the way, your Promises look a bit overcomplicated. If you decide you don't need those extra state changes, you can simplify to this:
export const FETCH_DB_BEGIN = 'FETCH_DB_BEGIN'
export const FETCH_DB_SUCCESS = 'FETCH_DB_SUCCESS'
export const FETCH_DB_FAILURE = 'FETCH_DB_FAILURE'
export const fetchDatabase = () => {
return dispatch => {
dispatch(fetchDbBegin());
const urls = [
'http://localhost:5000/profiles',
'http://localhost:5000/releases',
'http://localhost:5000/users'
];
const options = {
headers: header,
method: 'get',
mode: 'cors',
body: null,
}
const fetchJson = url => fetch(url, options).then(res => res.json());
Promise.all(urls.map(fetchJson))
.then(([profile, release, employee]) => {
dispatch(fetchDbSuccess({ profile, release, employee }));
})
.catch(err => {
dispatch(fetchDbFailure(err));
});
}
}
export const fetchDbBegin = () => ({
type: FETCH_DB_BEGIN
});
export const fetchDbSuccess = (data) => ({
type: FETCH_DB_SUCCESS,
payload: { data }
});
export const fetchDbFailure = (err) => ({
type: FETCH_DB_FAILURE,
payload: { err }
});
I am trying to do a sequence of fetching some API data and manipulating it as follows:
Fetch list of string addresses via API
Convert each of this string addresses to a geocoded location via API
Displaying these geocoded addresses as markers on a map
I am having some trouble with getting the timing with all these asynchronous activities right (I am pretty new to Javascript).
Here's what I have so far:
class Map extends Component {
constructor(props) {
super(props);
this.state = {
addresses = [],
geocodedAddresses = []
}
}
componentDidMount() {
const geocodedAddresses = []
fetch('.....')
.then(result => result.json())
.then(addresses => this.setState({ addresses }, function() {
this.state.addresses.forEach(address => {
Geocode.fromAddress(address).then(geocodedAddress => {
geocodedAddresses.push(geocodedAddress['results'][0]['geometry']['location'])
})
})
}))
console.log(geocodedAddresses) //Correctly contains the geocoded addresses
this.setState({ geocodedAddresses })
}
}
render() {
this.state.addresses.map(a => console.log(a)) //Evaluates correctly
this.state.geocodedAddresses.map(ga => console.log(ga)) //Yields nothing....
.....
}
}
So I don't quite understand why React does not re render when I do this.setState({ geocodedAddresses }) - Shouldn't react re render when I do setState?
There are a couple errors with your code. In the first place, the state object is being created with equals instead of colons:
this.state = {
addresses: [],
geocodedAddresses: []
}
In the second place, you should take into account that your code is asynchronous. Only when the promises generated by the call to Geocode.fromAddress resolve you have the data for your geocodedAddresses.
In the componentDidMount, you are console logging the geocodedAdresses and you report that you are seeing the right values. This is only because the log is update after the promises resolve. But when you do the console.log the value at that moment for geocodedAdresses is an empty array. And that is the value that is being inserted in the component state.
In order to set the correct value for the state, you should call setState when all your Geocode.fromAddress promises have resolved. In order to do that you can use Promise.all method.
So, your code would look like:
class Map extends Component {
constructor(props) {
super(props);
this.state = {
addresses: [],
geocodedAddresses: []
}
}
componentDidMount() {
fetch('.....')
.then(result => result.json())
.then(addresses => {
Promise.all(addresses.map(address => Geocode.fromAddress(address)))
.then(geocodedAddresses => {
this.setState({
addresses,
geocodedAddresses
})
});
}))
}
}
render() {
this.state.addresses.map(a => console.log(a)) //Evaluates correctly
this.state.geocodedAddresses.map(ga => console.log(ga)) //Yields nothing....
.....
}
}
Note that with this solution setState is only being called once.
Since all your state refers to addresses information, it could make sense to merge that information into a single key in the state. You could initialize your state in the constructor as:
this.state = {
addresses: []
};
And then, you could populate the state once all the promises resolve:
componentDidMount() {
fetch('.....')
.then(result => result.json())
.then(addresses => {
Promise.all(addresses.map(address => Geocode.fromAddress(address)))
.then(geocodedAddresses => {
const addressesState = addresses.map((address, i) => {
return {
address,
geocodedAddress: geocodedAddresses[i]
};
});
this.setState({ addresses: addressesState })
});
}))
}
}
You're right it's the async is slightly off. The console.log will populate after it "appears" in the console, but when setState has been called (and also when console.log has been called) its value is still [].
if you're using async/await you can wait for the fetch chain to completely finish, or put the setState within the then. With the setState callback, it's probably better to do the latter.
componentDidMount() {
const geocodedAddresses = []
fetch('.....')
.then(result => result.json())
.then(addresses => this.setState({ addresses }, function() {
this.state.addresses.forEach(address => {
Geocode.fromAddress(address).then(geocodedAddress => {
geocodedAddresses.push(geocodedAddress['results'][0]['geometry']['location'])
})
})
console.log(geocodedAddresses) //Correctly contains the geocoded addresses
this.setState({ geocodedAddresses })
}))
}}
or
componentDidMount = async () => {
const geocodedAddresses = []
let addresses = await fetch('.....').then(result => result.json())
addresses.forEach(address => { // assuming this is sync
Geocode.fromAddress(address).then(geocodedAddress => {
geocodedAddresses.push(geocodedAddress['results'][0]['geometry']['location'])
})
})
this.setState({ geocodedAddresses,addresses })
}}
I have the below code, where I have to get data from all the files in the same DB. Node.js is running at the backend. When I try the below code, I always get the last fetch, can anyone please help me how to fix this.
The below is from the react JS frontend.
componentDidMount() {
console.log("This Worked Sucessfully")
this.getDataFromDb();
if (!this.state.intervalIsSet) {
let interval = setInterval(this.getDataFromDb, 1000);
this.setState({ intervalIsSet: interval });
}
}
getDataFromDb = () => {fetch('http://172.24.78.202:3001/api/passed')
.then(data => data.json())
.then(res => this.setState({ passed: res.data }));
};
getDataFromDb = () => {fetch('http://172.24.78.202:3001/api/failed')
.then(data => data.json())
.then(res => this.setState({ failed: res.data }));
};
getDataFromDb = () => {fetch('http://172.24.78.202:3001/api/all')
.then(data => data.json())
.then(res => this.setState({ data2: res.data }));
};
render() {
const primaryColor = getColor('primary');
const secondaryColor = getColor('secondary');
const { passed, failed, data2 } = this.state
From what I see by your code, you seem to be re-writing your goGetDataFromDB two times. Try changing the names of each function or, the way you call them. You can also take advantage of Promise.all to group the results of each call into a single return handle.
Check this link for the documentation of Promise.all
You could refactor your current code to something like this:
class MyComponent extends React.Component {
componentDidMount() {
this.getDataFromDb();
if (!this.state.intervalIsSet) {
let interval = setInterval(this.getDataFromDb, 1000)
this.setState({intervalIsSet: true })
}
}
getDataFromDb = () => {
Promise.all([
'http://172.24.78.202:3001/api/passed',
'http://172.24.78.202:3001/api/failed',
'http://172.24.78.202:3001/api/all'
].map(url => (
fetch(url)
.then(data => data.json())
.then(res => res.data)
)
)).then(([passed, failed, data2]) =>
this.setState({ passed, failed, data2 })
);
}
render() {
//...
}
}
I tried to keep as much as your code as possible so you could notice the differences.
I hope this helps.
Not sure what i'm tyring to do is possible, but
state = {
characters: [],
planets: [],
search: "",
selectedCharacter: null
};
componentDidMount() {
this.fetchSomeData(
"https://rickandmortyapi.com/api/character/",
"characters"
);
this.fetchSomeData("https://rickandmortyapi.com/api/location", "planets");
}
fetchSomeData = (url, stateToSet) => {
fetch(url)
.then(res => res.json())
.then(data => this.setState({
[stateToSet]: data.results,
next: data.info.next,
prev: data.info.prev
}))
.catch(err => alert(err));
};
I have a characters page, and a planets page both obviously 2 different endpoints from the api
https://rickandmortyapi.com/api/character/
https://rickandmortyapi.com/api/location/
the problem i'm having:
when
componentDidMount(){
}
runs it's fetching both sets of data, then storing both
next page endpoints in the next state
"https://rickandmortyapi.com/api/location/?page=2"
"https://rickandmortyapi.com/api/character/?page=2",
i need to access next when clicking a button to reload the next page of data. (one component displays characters, for that one i need the "https://rickandmortyapi.com/api/character/?page=2",
and one component displays planets for that one i need "https://rickandmortyapi.com/api/character/?page=2",
)
how can i store these 2 separately?
i was thinking of trying to match them to their respective endpoints like this:
fetchSomeData = (url, stateToSet) => {
fetch(url)
.then(res => res.json())
.then(data => this.setState({
[stateToSet]: data.results,
**[stateToSet]next: data.info.next,**
prev: data.info.prev
}))
.catch(err => alert(err));
};
but i'm not sure you can incorporate the parameter into it like that.
You need to change a small thing in fetchSomeData like below:
fetchSomeData = (url, stateToSet) => {
fetch(url)
.then(res => res.json())
.then(data => this.setState({
[stateToSet]: data.results,
[`${stateToSet}-next`]: data.info.next,
[`${stateToSet}-prev`]: data.info.prev
}))
.catch(err => alert(err));
};
used [${stateToSet}-next] Template literals. Hope it helps and demo
This will print :
{
characters: Array[20],
characters-next: "https://rickandmortyapi.com/api/character/?page=2"
characters-prev: ""
planets: Array[20],
planets-next: "https://rickandmortyapi.com/api/location?page=2"
planets-prev: ""
search: "",
selectedCharacter: ""
}
and #Croolsby's answer also nice solution.
This should do it:
fetchSomeData = (url, stateToSet) => {
fetch(url)
.then(res => res.json())
.then(data => this.setState({
[stateToSet]: {
results: data.results,
next: data.info.next,
prev: data.info.prev
}
}))
.catch(err => alert(err));
};
Then you can access it like this:
this.state.characters.next
this.state.planets.next
The general lesson here is that you can have nested objects inside this.state.
For some reason, I am unable to update the state of my component with the data from my fetch request.
When I console.log, I can see that I am getting the data. I'm not sure what this could be
If this is a noob issue, please bear with me. I'm still learning.
Here is my code:
import React, { Component } from "react";
class Nav extends Component {
state = {
searchTerm: "",
posts: []
};
getPost = e => {
e.preventDefault();
const val = e.target.value;
this.setState({ searchTerm: val }, () => {
if (val !== "") {
fetch(
`http://www.reddit.com/search.json?q=${val}&sort=relevance&limit=25`
)
.then(res => res.json())
.then(data => console.log(data.data))
//.then(data => this.setState({ posts: data.data }))
//.then(console.log(this.state.posts))
.catch(err => console.log(err));
}
});
};
Actually everything is right and going well, just your logging is wrong.
.then(console.log(this.state.posts))
That logs the state now and passes the result of console.log() (undefined) to the .then chain as a callback which is obviously wrong. I guess you meant:
.then(() => console.log(this.state.posts))
But that still won't work as setState does not trigger a state update immeadiately, but somewhen. After that it calls the second argument as a callback, so you should log then:
.then(data => this.setState({ posts: data.data }, () => {
console.log(this.state.posts);
}))
Altogether:
const response = fetch(
`http://www.reddit.com/search.json?q=${val}&sort=relevance&limit=25`
).then(res => res.json());
// PS: I would not build up a chain if the logic is not really "chained"
response.then(data => console.log(data.data));
response.then(data => this.setState({ posts: data.data }, () => console.log(this.state.data)));
response.catch(err => console.log(err));