Data not getting displayed from fetching from an api - javascript

First i declared an empy array inside states:
But Data is not getting displayed it is showing some error :
also it is show an error when setting state as
Unhandled Rejection (TypeError): undefined is not an object (evaluating 'this.setState')
and it is pointing towards the setState line
can someone solve this

I dont understand why you use JSON.stringify here
return JSON.stringify(jsonData);
Because you already convert your data into json here
return response.json();
Also calling API to get data is using componentDidMount life cycle hook. Try to change your code like this
componentDidMount() {
fetch('http://localhost:3000/"my end point in api"')
.then(function(response) {
return response.json();
})
.then(function(jsonStr) {
this.setState({ CourseTitle: jsonStr.course_title });
});
}

As per the official docs,componentWillMount is depreciated,try calling it in componentDidMount.
componentDidMount(){
fetch('http://localhost:3000/"my end point in api"')
.then(function(response) {
return response.json();
}).then(function(jsonData) {
return JSON.stringify(jsonData);
})
.then(function(jsonStr) {
this.setState({ CourseTitle: jsonStr.course_title });
alert(jsonStr.course_title);
});
}
And while printing ,put a condition like
console.log("printing",this.state.courseTitle?this.state.courseTitle:"not yet fetched")

Not getting why are you fetching like mentioned in post instead of like this...
componentDidMount() {
fetch('http://localhost:3000/"my end point in api"')
.then(function(response) {
return response.json();
})
.then(function(jsonStr) {
this.setState({ CourseTitle: jsonStr.course_title });
alert(jsonStr.course_title);
});
}
and also console your data before rendering
And it's recommended to call api in componentDidMount lifecycle method as per official documentation of Reactjs

Three things:
Try to follow the recommendations of others, change componentWillMount by componentDidMount.
Also, follow the recommendation of removing the JSON.stringify(jsonData) because you are converting the object in a string and after that, you are trying to access object properties (and they don't exist).
And finally, it is a common error in JavaScript to misunderstand what is the meaning of this in different contexts. For example, take a look at the next snippet:
const obj = {
say() {
console.log(this.toString());
},
sayDelayed() {
setTimeout(function() {
console.log(this.toString());
}, 500);
}
};
obj.say();
obj.sayDelayed();
As you can observe, this inside the setTimeout is not referencing the object anymore and this is because setTimeout is referencing another object (most probably window in non-strict mode).
In your case, this inside the fetch is not referencing the class anymore. Try to store the scope of the class in a variable before executing the fetch and use this variable instead of this to call setState. Check the next snippet:
const obj = {
fetch() {
const myObject = this;
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(function(response) {
return response.json();
})
.then(function(jsonStr) {
console.log('wrong this', this.toString());
console.log('right this', myObject.toString());
});
}
};
obj.fetch();
You need to refactor your code into something like this:
componentDidMount() {
const cls = this;
fetch('http://localhost:3000/"my end point in api"')
.then(function(response) {
return response.json();
})
.then(function(jsonStr) {
cls.setState({ CourseTitle: jsonStr.course_title });
});
}

Related

How to search an array of objects obtained by axios for an id? Vue 2

I am trying to verify if the user is inside that list that I capture by axios, the issue is that I have used the FILTER option but it always returns undefined or [], being that if the user exists in that array.
I can't think what else to do, because I validate if it is by console.log() the variable with which I ask and if it brings data.
created() {
this.getStagesDefault()
this.getSalesman()
this.getStagesAmountByUser()
},
methods: {
async getSalesman(){
const { data } = await axios.get('salesman')
this.employees = data.data
},
getStagesAmountByUser(){
console.log(this.user['id'])
var objectUser = this.employees.filter(elem => {
return elem.id === this.user['id']
})
console.log(objectUser)
},
Console
Vue data
The method getSalesman is asynchronous, meaning that getStagesAmountByUser will start executing before getSalesman finishes.
Two ways to fix the problem:
Await the getSalesman method, but you have to make the created method async as well. Change the code as follows:
async created() {
this.getStagesDefault()
await this.getSalesman()
this.getStagesAmountByUser()
}
Attach a .then to the getSalesman function, and start the next one inside the .then. Change the code as follows:
created() {
this.getStagesDefault()
this.getSalesman().then(() => this.getStagesAmountByUser())
}
getSalesman is an async method. At the time of the filter, the array being filtered is still empty.
this.getSalesman() // this runs later
this.getStagesAmountByUser() // this runs right away
Have the methods run sequentially by awaiting the async method:
await this.getSalesman()
this.getStagesAmountByUser()
You can avoid the inefficient clientside filtering if you pass the id to the backend and only select by that id.
Additionally, created only gets called once unless you destroy the component which is also inefficient, so watch when user.id changes then call your method again.
Plus don't forget you must wrap any async code in a try/catch else you will get uncaught errors when a user/salesman is not found etc, you can replace console.error then with something which tells the user the error.
{
data: () => ({
employee: {}
}),
watch: {
'user.id' (v) {
if (v) this.getEmployee()
}
},
created() {
this.getEmployee()
},
methods: {
getEmployee() {
if (typeof this.user.id === 'undefined') return
try {
const {
data
} = await axios.get(`salesman/${this.user.id}`)
this.employee = data.data
} catch (e) {
console.error(e)
}
}
}
}

Array is not array anymore when passed into Vuex action function

EDIT: Added extra code in the filterEvents snippet for more context.
I'm not quite understanding what's going on with my code. I'm trying to pass an array into an action function inside of my Vuex store. If I return a Promise inside of that action function, then the parameter being passed isn't of type Array and is instead an Object, which results in the reject() error that I have for the Promise.
Here's some code for context:
filterEvents({ commit }, events) {
console.log(Array.isArray(events)); //this ends up false
console.log(events);
return new Promise((resolve, reject) => {
if (!Array.isArray(events)) {
reject("Invalid argument: is not of type Array.");
}
let filtered = events.filter((event) => {
let now = new Date();
let event_stop = new Date(event.stop_time);
if (event_stop >= now || event_stop == null) {
return event;
}
});
resolve(filtered);
});
}
Here's where I call filterEvents; inside of getEvents;
getEvents({ state, commit, dispatch }, searchParams) {
.....
eventful.getEvents(searchParams).then(async (res) => {
.....
console.log(Array.isArray(res.data.events.event)); //this ends up true
console.log(res.data.events.event);
/* where I call it */
await dispatch("filterEvents", res.data.events.event).then((res) => {
.....
});
}).catch((err) => {
.....
});
}
Here's the output from the Chrome developer console. First two outputs are from getEvents and last two are from filterEvents
Would really like an explanation as to why this is the case. I'm going to bet it's something small, but it's 3 a.m. at the moment and my brain can't wrap around why it's not of type Array when passed into filterEvents.
I always try to check the length prop of the array which helps me out in such cases.
...
return new Promise((resolve, reject) => {
if (!Array.isArray(events) && !events.length) {
reject("Invalid argument: is not of type Array.");
}
.....
});
...
I finally understood what my issue was after taking another look at the object that was being logged on the console. I did not know that Vuex actions HAD to have two arguments if you want to pass in a payload into that function. For example, I initially did this
filterEvents(events) {
.....
}
but what I really needed to do was
filterEvents(context, events) {
.....
}
The context argument is the object that allows you to do things such as commit and dispatch. I usually destructure the context object (i.e. { commit, dispatch} ), so I for some reason never thought twice about it. You don't have to destructure the context object to use commit and dispatch; if you don't it would just be like
context.commit('function', payload);

How to assign array fetched from an API to data property in Vue.js?

I am trying to fetch news articles from an external source, it returns JSON object. I want to assign its articles property to a variable in my component. Somehow this error is occurring.
Uncaught (in promise) TypeError: Cannot set property 'articles' of undefined
Any suggestions on how to overcome this problem?
export default {
name: "blog",
data() {
return {
articles: [],
};
},
mounted() {
// API call
this.fetchnews();
},
methods: {
fetchnews(){
fetch(
"----------------------news link-------------------------"
)
.then(function(response) {
return response.json();
})
.then(function(json_data) {
//console.log(typeof(json_data))
this.articles = json_data.articles
});
}
}
};
As the first contributor properly noticed - the issue is this.articles inside your latest function doesn't really point to what you need.
If you are limited to ES5 then stick to the first answer.
However if you can use ES6 then simply get advantages of short syntax:
export default {
name: "blog",
data() {
return {
articles: [],
};
},
mounted() {
// API call
this.fetchnews();
},
methods: {
fetchnews(){
fetch("----------------------news link-------------------------")
.then(response => response.json())
.then(json_data => this.articles = json_data.articles);
}
}
};
in this case this will properly point to the outer scope.
Also why do you need two then()? You could collapse them into one:
.then(response => this.articles = response.json().articles);
using function keyword creates new scope. if you use arrow syntax like () => {} you can use parent scope and set articles via this.articles
fetchnews(){
fetch()
.then((response) => {
return response.json();
})
.then((json_data) => {
this.articles = json_data.articles
});
}
inthis.articles: here this refers to the function not vue instance , so you may define this outside the function like:
let This=this
and inside your function :
This.articles = json_data.articles
This here refers to vue instance
javascript function as global scope make sure use to assign function to variables

Vue JS component not reactive when retrieving data using fetch

I have a strange problem in my Vue application.
The component looks like this:
...
<template v-for="foo in foos">
<Elm v-if="foo.visible" :key="foo.label" :bar="foo" />
</template>
...
"Elm" is a value in an object, retrieved from a JSON file.
The component is reactive if I get the JSON file locally:
<script>
import datas from "datafile.json";
...
methods: {
fillFoos() {
datas.forEach(data => {
this.foos.push(data)
})
}
},
mounted: {
this.fillFoos()
}
...
</script>
But when I retrieve the file remotely using fetch, the component is no longer reactive and no longer disappears when the foo.visible value is changed :
<script>
methods: {
getDataFromApi() {
return new Promise((resolve, reject) => {
fetch(this.apiUrl)
.then(response => {
return response.json();
})
.then(jsonResponse => {
resolve(jsonResponse);
})
.catch(e => {
...
})
})
},
fillFoos() {
this.getDataFromApi()
.then(response => {
response.forEach(data => {
this.foos.push(data);
});
});
}
},
mounted: {
this.fillFoos()
}
...
</script>
In both cases the "foos" array is correctly filled, the only difference is that the v-if directive seems to be broken in the second case.
To be more precise, the display is done correctly at initialization (foo.visible is true for all the elements and they're all displayed), but in case of a later change of the foo.visible value, they don't disappear.
I can't find what's wrong...
I believe the issue you are having is that the method getDataFromApi is returning a promise, but when you consume it in fillFoos the promise is not awaited, instead you call forEach on it.
You need to use the getDataFromApi().then(x => {}) syntax to resolve the promise, or alteratively you can use async await.
You can try something like this
methods: {
async getDataFromApi() {
const response= await fetch(this.apiUrl);
return response.json();
},
async fillFoos() {
try {
await foos = this.getDataFromApi();
this.foos = foos;
} catch(error) {
//handle error.
}
}
}
Someone posted a response very close to the solution yesterday but deleted it, I don't know why.
The problem was that I stored the fetch response in a variable in the data section, before using it to fill in the "foos" table
data: function() {
return {
dataFromApi: null
}
}
By removing this variable, and thus creating it on the fly after the fetch, everything works normally.... I didn't specify that I stored the answer in this variable because I didn't think it could be related... Morality: always specify everything !

How to handle vuex store to fetch data from rest api?

I'm using Vuex to handle my application state.
I need to make an Ajax Get request to a rest api and then show some objects list.
I'm dispatching an action that loads this data from the server but then I don't know how to handle it on the component.
Now I have this:
//component.js
created(){
this.$store.dispatch("fetch").then(() => {
this.objs = this.$store.state.objs;
})
}
But I don't think that the assignment of the incoming data to the local property is the correct way to handle store data.
Is there a way to handle this better? Maybe using mapState?
Thanks!
There are many ways you can do it, you must experiment and find the one that fits your approach by yourself. This is what I suggest
{ // the store
state: {
something: ''
},
mutations: {
setSomething (state, something) {
// example of modifying before storing
state.something = String(something)
}
},
actions: {
fetchSomething (store) {
return fetch('/api/something')
.then(data => {
store.commit('setSomething', data.something)
return store.state.something
})
})
}
}
}
{ // your component
created () {
this.$store
.dispatch('fetchSomething')
.then(something => {
this.something = something
})
.catch(error => {
// you got an error!
})
}
}
For better explanations: https://vuex.vuejs.org/en/actions.html
Now, if you're handling the error in the action itself, you can simply call the action and use a computed property referencing the value in the store
{
computed: {
something () { // gets updated automatically
return this.$store.state.something
}
},
created () {
this.$store.dispatch('loadSomething')
}
}

Categories