Using this.setstate with Fetch API - reactjs

I'm trying to use a fetch call to retrieve some API data. Unfortunately I cannot use this to set the state.
consructor() {
this.state = {
weather: ""
};
this.search = this.search.bind(this);
}
search(postalCode) {
const url = `https://api.weatherbit.io/v2.0/forecast/daily?&postal_code=${postalCode}&key=${API_KEY}&days=7`;
fetch(url)
.then(response => response.json())
.then(data => console.log(data));
}'
This code works fine, and prints an object to the console containing the correct data.
consructor() {
this.state = {
weather: ""
};
this.search = this.search.bind(this);
}
search(postalCode) {
const url = `https://api.weatherbit.io/v2.0/forecast/daily?&postal_code=${postalCode}&key=${API_KEY}&days=7`;
fetch(url)
.then(response => response.json())
.then(data => this.setState({weather: data}))
.catch(error => console.log(error));
}
As soon as I try to use the data to update my state, I get the "this.setState is not a function" error. I've bound the search function in the constructor.
I've even tried to bind it like this:
fetch(url)
.then(response => response.json())
.then(data => this.setState({ weather: data })).bind(this);
This doesn't work either. Anyone have any ideas?

I can't see your class definition, but I believe that using the state requires you to extend Component
The issue you are getting is that this component doesn't have the state functions in it.
Here's some code from a working project of mine using similar fetches, returned by the services:
type HomeProps = {}
type State = {
projects: ProjectModel[],
skills: SkillModel[],
resume: JobModel[]
}
export default class Home extends Component<HomeProps, State> {
private projectService: ProjectService;
constructor(props: HomeProps) {
super(props);
this.projectService = new ProjectService();
this.state = {
projects: []
}
}
componentDidMount() {
this.getProjects();
}
private getProjects() {
this.projectService.retrieveItems().then(projects => {
this.setState({projects});
})
}
Here is the service:
export default class ProjectService {
url = "http://api.flynndev.us";
async retrieveItems() {
return fetch(`${this.url}/projects/all`)
.then(response => response.json());
}
async getItem(itemLink: string) {
return fetch(`${this.url}/projects/${itemLink}`)
.then(response => response.json());
}
}
Note: I'm using Typescript. Feel free to ignore the typing stuff for regular React

Related

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');

Setting state object dynamically using the data returned using Promise.all and fetch API : React+Typescript

I am using fetch API and promise.all for a scenario where I am passing an array of URL'S from where I am fetching the data. The data retrieved from all the above URL'S needs to be set to the state object.
Say I have an array of 5 URL's , the result returned by these must be
assigned to the 5 different values inside my state object.
Using React along with typescript.
Help would be appreciated.
This is what I have tried so far
import * as React from 'react';
const urls = [ 'http://localhost:3001/url1',
'http://localhost:3001/url2',
'http://localhost:3001/url3',
]
interface IState {
test: [],
result: [],
returnVal: []
}
export default class App extends React.Component<{},IState> {
constructor(props:any)
{
super(props);
this.state = {
test: [],
result: [],
returnVal: []
}
checkStatus(response:any) {
if (response.ok) {
return Promise.resolve(response);
} else {
return Promise.reject(new Error(response.statusText));
}
}
parseJSON(response:any) {
return response.json();
}
setData(data:any){
Object.entries(this.state).forEach(([key], index) => {
this.setState({ [key]: data[index] })
});
}
componentDidMount()
{
Promise.all(urls.map(url =>
fetch(url)
.then(this.checkStatus)
.then(this.parseJSON)
.catch(error => console.log('There was a problem!', error))
))
.then(data => {
this.setData(data);
})
}
render() {
return(
//some rendering code
)
}
}
Need to set the data returned from promise to the state object variables.
Promise.all(urls.map(url =>
fetch(url)
.then(this.checkStatus)
.then(this.parseJSON)
))
.then(jsons => {
var newState = {};
var index = 0;
for(var key in this.state)
newState[key] = jsons[index++];
this.setState(newState);
})

Load data from function to state

I have function which load all data from API. I would like to use that function to pass that data to array. But i cannot figured out how to do that.
I have already tried to put that function inside of state of my array, because I do not know how to use that function
function getRoles {
const url = 'URLTOENDPOINT'
fetchUtils.fetchJson(url, {
method: "GET",
})
.then(response => {
Object.keys(response.json.value).forEach(function (key) {
var object = response.json.value[key];
names.push(object.Name);
})
});
return names;
}
Simply i want to load data from getRoles function to this array inside state:
class MultipleSelect extends React.Component {
state = {
name: [
'Oliver Hansen',
'Van Henry',
'April Tucker'
]
};
...
Expected result should be MultipleSelect with default data loaded from API.
Any ideas how to use that function or what should be improved?
componentDidMount(){
this.setState({name:getRoles()})
}
you can try this way also function return directly set it to state variable
class MultipleSelect extends React.Component {
state = {
name: [
'Oliver Hansen',
'Van Henry',
'April Tucker'
]
};
getRoles() {
const url = 'URLTOENDPOINT'
var names
fetchUtils.fetchJson(url, {
method: "GET",
})
.then(response => response.json())
.then((res) => {
console.log(res)
names = res.value.map((data)=>(data.Name))
})
return names;
}
componentDidMount(){
this.setState({name:this.getRoles()})
}
}
Addition to my comment:
componentDidMount() {
fetch(
`https://xxx`,
)
.then(res => res.json())
.then(val=> {
this.setState({ ... });
});
}

SetState and React-Native lifecycle

I'm taking my first steps with React-Native. I can not understand why with the following code I get the value "data" = [] inside _refreshData (console.log(this.state.data);)
I have this code from Learning React Native book:
class SimpleList extends Component {
constructor(props) {
super(props);
console.log("Inside constructor");
this.state = { data: [] };
}
componentDidMount() {
console.log("Inside componentDidMount");
this._refreshData();
}
...
_refreshData = () => {
console.log("Inside_refreshData");
console.log(NYT.fetchBooks());
NYT.fetchBooks().then(books => {
this.setState({ data: this._addKeysToBooks(books) });
});
console.log("This is data: ");
console.log(this.state.data);
};
function fetchBooks(list_name = "hardcover-fiction") {
console.log("Inside fetchBooks");
let url = `${API_STEM}/${LIST_NAME}?response-format=json&api-
key=${API_KEY}`;
return fetch(url)
.then(response => response.json())
.then(responseJson => {
return responseJson.results.books;
})
.catch(error => {
console.error(error);
});
}
Debugging (with console.log) I see that "data" = [] even if I just called the setState and from the log I see that the fetch returned my values ...
This is the call log:
Can you explain why please?
Thanks in advance.
Ok, first it's promise and asynchronous, and it's not guaranteed that when you log your data also you receive the data, so when you are in componentDidMount and call console.log(this.state.data); maybe the data is not returned yet. think it took 2000 milliseconds to return the data from api. so you call
NYT.fetchBooks().then(books => {
this.setState({ data: this._addKeysToBooks(books) });
});
and then this code as I said took 2000 milliseconds, but as I said you immediately log the data so, because at this time data is not filled you see the empty array.but if you want to see the data you can log it here :
NYT.fetchBooks().then(books => {
console.log(books);
this.setState({ data: this._addKeysToBooks(books) });
});

Undefined object in react js

I'm getting the following error:
Uncaught ReferenceError: videos2 is not defined
In this app:
class App extends Component {
constructor(props) {
super(props)
this.state = {
videos2:[],
selectedVideo:null
}
this.DMSearch()
}
DMSearch(){
fetch("https://api.dailymotion.com/videos?fields=description,id,thumbnail_url,title,&limit=5&search=cars")
.then(response => response.json())
.then(data=>this.setState({
videos2:data.videos2,
selectedVideo:videos2[0]}))
console.log(videos2)
}
render () {
const {videos2}=this.state
return (
<div>
<SearchBar onSearchTermChange= {DMSearch}/>
<VideoDetail video={this.state.selectedVideo}/>
<VideoList
onVideoSelect={selectedVideo=>this.setState({selectedVideo})}
videos2={this.state.videos2}/>
</div>
)
}
}
Therefore Im wondering where should I define videos2 apart from where it is defined already. Anyone could point me out to the part of the component that might be causing the error?
EDIT: Actually it had to do with the way api json was shaped.
This is the proper way to fetch the list from json:
this.setState({
videos2: videos2.list,
selectedVideo: videos2[0]
});
In DMSearch function there is no videos2 variable defined. You probably want:
.then(data => {
this.setState({
videos2: data.videos2,
selectedVideo: data.videos2[0]
});
console.log(data.videos2);
});
or
.then(data => {
const { videos2 } = data;
this.setState({
videos2,
selectedVideo:videos2[0]
});
console.log(videos2);
});
In DMSearch, videos2 is undefined.
DMSearch() {
fetch("https://api.dailymotion.com/videos?fields=description,id,thumbnail_url,title,&limit=5&search=cars")
.then(response => response.json())
.then(data => {
let videos2 = data.videos2; //define it here
this.setState({
videos2: videos2,
selectedVideo: videos2[0] // <-- this line will throw error
})
})
console.log(videos2) // <-- and this too
}

Resources