Is there a way to use template literals for fetch request? - reactjs

I'd like to define a function that makes a fetch request based on its parameter so I can simplify the fetch calls inside componentDidMount(). I've tried the code below but it gives an Unexpected token error for the setState function. Is there a way to use the parameter of the function inside then()?
constructor() {
super();
// cases in date order from 1 to 5. Cases5 is the latest.
this.state = {
cases1: [],
cases2: [],
cases3: [],
cases4: [],
cases5: [],
};
}
componentDidMount() {
fetch("/cases/1")
.then((res) => res.json())
.then((cases1) => this.setState({ cases1 }));
fetch("/cases/2")
.then((res) => res.json())
.then((cases2) => this.setState({ cases2 }));
fetch("/cases/3")
.then((res) => res.json())
.then((cases3) => this.setState({ cases3 }));
fetch("/cases/4")
.then((res) => res.json())
.then((cases4) => this.setState({ cases4 }));
fetch("/cases/5")
.then((res) => res.json())
.then((cases5) => this.setState({ cases5 }));
}
fetchCaseData = (index) => {
fetch(`/cases${index}`)
.then((res) => res.json())
.then((`cases${index}`) => this.setState({ `cases${index}` }));
}

You can't simply declare a dynamic name for a variable. You can however set dynamic keys for an object using brackets notation but not object shorthand syntax
Updated code will look like below
.then((val) => this.setState({ [`cases${index}`]: val }));

Related

How to modify data received from fetch Request in React

The question is how to modify data received from fetch Request in React?
1)I get the result of a fetch request and put in into state
2)To work with the data I need to modify it (add an ID and a boolean to each item of the array inside one of the objects of the coming array.
I tried the following way, but it doesn't work:
useEffect(() => {
fetch(
"https://opentdb.com/api.php?amount=5&category=10&difficulty=easy&type=multiple"
)
.then((res) => res.json())
.then((data) => data.map(question => ({
...question,
incorrect_answers: question.incorrect_answers.map(answer => {...answer, isSelected: false})
})));
}, []); ```
Your data is a object so you can't map over it. The data object has a property results which contains the array of questions.
data.results.map((question) => ({ ... })
Then the first time you correctly surround the object you return with () but when you map over the incorrect_answers you don't.
incorrect_answers: question.incorrect_answers.map((answer) => ({
...answer,
isSelected: false,
}));
Full code
useEffect(() => {
fetch(
"https://opentdb.com/api.php?amount=5&category=10&difficulty=easy&type=multiple"
)
.then((res) => res.json())
.then((data) =>
data.results.map((question) => ({
...question,
incorrect_answers: question.incorrect_answers.map((answer) => ({
answer,
isSelected: false,
})),
}));
);
}, []);

React JS fetch data from multiple API function

I was tasked to create a front-end for a poc. I am not a front end developer but I chose to use React JS.
There is only one page fetching data from multiple API endpoints. API endpoints return a simple json object.
I managed to get that to work however my code is ugly af and I want to create a function to handle all of that but I can't seem to get it right. Here's my code
export default class Dashboard extends React.Component {
constructor(props) {
super(props);
this.state = {
group1: [],
group2: [],
group3: [],
isLoaded: false,
}
}
componentDidMount() {
const group1_url = "http://localhost/api/1"
const group2_url = "http://localhost/api/2"
const group3_url = "http://localhost/api/3"
fetch(group1_url)
.then(res => res.json())
.then(json => {
this.setState({
group1: json,
})
});
fetch(group2_url)
.then(res => res.json())
.then(json => {
this.setState({
group2: json,
})
});
fetch(group3_url)
.then(res => res.json())
.then(json => {
this.setState({
group3: json,
})
});
}
I am trying to create a function like this:
function fetch_data(url, state) {
fetch(url)
.then(res => res.json())
.then(json => {
this.setState({
state: json,
})
});
}
var group1 = fetch_data(group1_url, group1);
So far no joy. How can I create a function to fetch data and set a state in js?
Alternatively how can I make my code look better? Or is there something else I should use/look into?
Pass a string as the second parameter, and use a computed property:
function fetch_data(url, state) {
fetch(url)
.then(res => res.json())
.then(json => {
this.setState({
[state]: json,
})
});
}
fetch_data(group1_url, 'group1');
I'd also highly recommend catching errors - possible unhandled rejections should be avoided whenever possible.
You might want to use Promise.all to wait for all groups to load:
const dataSources = {
group1: 'http://localhost/api/1',
group2: 'http://localhost/api/2',
group3: 'http://localhost/api/3',
};
Promise.all(
Object.entries(dataSources).map(([propertyName, url]) => (
fetch(url)
.then(res => res.json())
.then((result) => {
this.setState({
[propertyName]: result
})
})
))
)
.then(() => {
this.setState({ isLoaded: true })
})
.catch((error) => {
// handle errors
})
(also note that your json argument is not a JSON - JSON is only a format that exists with strings. Something that has been deserialized is just a plain object or array. Better to call it something less misleading, like result as I did)
You could try Promise.all
Promise.all takes an array of promises (it technically can be any iterable, but is usually an array) and returns a new promise.
const points = [
"http://localhost/api/1",
"http://localhost/api/2",
"http://localhost/api/3",
];
const responses = await Promise.all(points.map((point) => fetch(point)));
const data = await Promise.all(responses.map((response) => response.json()));
const [group1, group2, group3] = data;
this.setState({
group1,
group2,
group3,
});
Just remember to wrap this logic in an async function
You can do something like this.
function fetch_data(url, state) {
fetch(url)
.then(res => res.json())
.then(json => {
this.setState({
[state]: json,
})
});
}
var group1 = fetch_data(group1_url, 'group1');

Forming an array from two joined apis in react

I am attempting to create a set of joined data from two apis that I would love to implement in a table, apparently I am not getting the expected result.
The logic behind my code is :
Get the data from the first api
loop through each element in the data to get a specific data from the second api depending with the id of each element.
Create a new key to each element, each data obtained in second api as a value.
the resulting data is set in the state.
I have been able to accomplish step 1 to 3 except step 4.
class App extends React.Component{
constructor(props){
super(props);
this.state = {
Data: [],
};
}
componentDidMount() {
fetch('http://localhost:8000/tasks?format=json')
. then(res => res.json())
.then(data => data['results'].forEach(element => {
fetch(`http://localhost:8000/task/${element.id}/runs`)
.then(res => res.json())
.then(data2 => element['rundata'] = data2)
.then(this.state.Data.push(element))
}))
.catch(err => console.log(err))
}
render(){
console.log('data', this.state.Data)
return(
)
}
}
export default App;
You can only update react state with setState method.
Try the below code.
then(this.setState(prev => ({
data: [...prev, element]
})
Well the main reason for the issue is that you are not using setState correctly.
you need to use setState, and you need to not mutate the state. Always copy the old state and then modify it.
The second issue which may occur is that your forEach method is not synchronised.
Try changing your componentDidMount to this:
componentDidMount() {
fetch('http://localhost:8000/tasks?format=json')
.then(res => res.json())
.then(data => {
for (const element of data['results']) {
fetch(`http://localhost:8000/task/${element.id}/runs`)
.then(res => res.json())
.then(data2 => element['rundata'] = data2)
.then(this.setState({ Data: [...Data, element] })
}
}
.catch(err => console.log(err))
}
this.setState(prevState => ({
myArray: [...prevState.myArray, "new value"]
}))
in the other hand, i don't recommend to loop in fetch, either you use a post method and loop throw all the ids in your server side and return an array, or if you have a light database you get all the result and do a loop in your client side.
When you're setting data into the state in React you only need to use this.setState().
So for your case you only have to run this.setState({ Data: element }).
Maybe this :
componentDidMount() {
fetch('http://localhost:8000/tasks?format=json')
.then(res => res.json())
// create array of promise
.then(data => data.results.map(element => {
return fetch(`http://localhost:8000/task/${element.id}/runs`)
.then(res => element.rundata = res.json())
.cath(err => console.log(err))
}))
.then((arrayPromise) => {
Promise.all(arrayPromise)
.then(resolved => this.setState({data:resolved}))
})
.catch(err => console.log(err))
}
render(){
console.log('data', this.state.data)
return(
<></>
)
}
}

Assigning return values from api to state in React

Hi I am trying to call an api assign the returned values to a state object in React, the API is returning values but the values are not being set to state, not understanding what's the reason thank you
handleDDLCommunityChange = event => {
let filesFromApi = []; // ["file1", "file2", "file3", "file4"];
fetch('https://localhost:44352/api/files/Community-1')
.then((response) => {
return response.json();
})
.then(data => {
filesFromApi = data.map(file => { return { value: file, display: file } });
}).catch(error => {
console.log(error);
debugger;
});
console.log(filesFromApi);
this.setState({
files: filesFromApi.map(file => {
return {
fileName: file,
checked: false
};
})
});
};
fetch is an async method. An async method dispatches an action with the callbacks and unblocks following code branch from executing. The callbacks are then used to act on completion (success or failure) of the async method execution.
As you are calling the setState outside of the callbacks of the fetch call's chain, it's not guaranteed to run after the fetch call is done. As Sudheer has pointed out in their comment, you should try to set the state in a then block of the fetch chain.
warning: untested code
handleDDLCommunityChange = event => {
let filesFromApi = []; // ["file1", "file2", "file3", "file4"];
fetch('https://localhost:44352/api/files/Community-1')
.then(response => response.json())
.then(data => {
filesFromApi = data.map(file => ({ value: file, display: file });
this.setState({
files: filesFromApi.map(file => ({
fileName: file,
checked: false
})
})
});
}).catch(error => {
console.log(error);
debugger;
});
};

ReactJS setState when all nested Axios calls are finished

I have a problem with updating my state from nested axios call inside forEach loop:
constructor(props) {
super(props);
this.state = {
isLoaded: false,
items: []
};
//Binding fetch function to component's this
this.fetchFiles = this.fetchFiles.bind(this);
}
componentDidMount() {
this.fetchFiles();
}
fetchFiles() {
axios.get('/list')
.then((response) => {
var items = response.data.entries;
items.forEach((item, index) => {
axios.get('/download'+ item.path_lower)
.then((response) => {
item.link = response.data;
})
.catch(error => {
console.log(error);
})
});
this.setState(prevState => ({
isLoaded: true,
items: items
}));
console.log(this.state.items);
})
.catch((error) => {
console.log(error);
})
}
The idea is to get all items from Dropbox using it's API (JavaScript SDK)
and then for each item I also need to call different API endpoint to get a temporary download link and assign it as a new property. Only after all items will get their links attached I want to setState and render the component. Could somebody please help with this, I spend already multiple hours fighting with promises :S
You could use Promise.all to wait for multiple promises. Also keep in mind that setState is async and you wont see immediate changes. You need to pass a callback.
fetchFiles() {
axios.get('/list')
.then((response) => {
var items = response.data.entries;
// wait for all nested calls to finish
return Promise.all(items.map((item, index) => {
return axios.get('/download'+ item.path_lower)
.then((response) => {
item.link = response.data;
return item
});
}));
})
.then(items => this.setState(prevState => ({
isLoaded: true,
items: items
}), () => console.log(this.state.items)))
.catch((error) => {
console.log(error);
})
}
Try making the fetchfiles() function as an asynchronous method by adding the async keyword.Now, we have to wait till the items to get their download link, so add a await keyword before that line which makes the code to wait till the axios call gets completed.
async function fetchFiles() {
axios.get('/list')
.then(async function(response){
var items = response.data.entries;
await items.forEach((item, index) => {
axios.get('/download'+ item.path_lower)
.then((response) => {
item.link = response.data;
})
.catch(error => {
console.log(error);
})
});
this.setState(prevState => ({
isLoaded: true,
items: items
}));
console.log(this.state.items);
})
.catch((error) => {
console.log(error);
})
}
I haven't tested the code, but it should probably work.

Resources