Axios and VueJS, function(response) is not setting a list - vuejs2

I have an request to get some data and add it to a variable,
When I use:
.then(function(response) {
this.persons = response.data;
});
It does not assign response.data to this.persons but when I do the following:
.then(response => this.persons = response.data);
It assigns it fine to use. Please see the js fiddle:
https://jsfiddle.net/trhhtyxr/2/

As I have explained it here, arrow syntax does not bind it's own this, arguments, super, or new.target. Arrow functions are always anonymous. These function expressions are best suited for non-method functions.
Scope of this changes inside a function() block and it does not refer to the currently executing function, while with arrow function, this refers to the currently executing function only.

Related

Property accessed during render but not defined in VUE

I receive an error when I load the page
My code looks like this :
<script>
methods: {
getPicture() {
var base = this;
axios
.get("http://localhost:3000/pictures/" + this.username)
.then(function (response) {
const { pictureData } = response.data;
base.studentImage = "data:image/jpg; base64," + pictureData;
console.log(base.studentImage);
});
},
}, //END OF METHOD
</script>
I want to display image in My front end. Where do I make a mistake ? Thank you in advance.
Welcome to Stack Overflow!
Sadly, I can't comment on this for more clarity, so my response has to be an answer.
This error occurs because the DOM and/or Vue Instance and/or Template uses the value "studentImage" before it is made available or defined incorrectly.
I recommend removing the variable "base" because Vue is very picky with its use of "this," so you usually wanna stay away from explicitly assigning "this" to a variable. As commented by Estus Flask below, this even applies to common JS. Check here for more information on referring to the correct "this" in JS.
Honestly, from what I can see with the code you've shared, that's likely the cause of your error. You'll want to define "studentImage" within your data object so Vue is aware of its existence.
Here is an example:
data: () => ({
studentImage: '',
}),
methods: {
getPicture() {
axios
.get("http://localhost:3000/pictures/" + this.username)
.then(function (response) {
const { pictureData } = response.data;
this.studentImage = "data:image/jpg; base64," + pictureData;
console.log(this.studentImage);
});
},
},
If it is not, then you'll want to make sure you're checking for "studentImage" before using it within the instance and or calling the method "getPicture," in the appropriate lifecycle.
I'm answering this assuming you're using the latest version of Vue, version three.
If this helps, please let me know!

What is the syntax for watch in the vue 3 composition API?

https://stackoverflow.com/a/63800832/1497720
Typical watch syntax is watch(<variable>, <function to handle>)
but in this answer: https://stackoverflow.com/a/63800832/1497720
we have
watch(() => route.name, () => {
console.debug(`MyCoolComponent - watch route.name changed to ${route.name}`);
// Do something here...
// Optionally you can set immediate: true config for the watcher to run on init
//}, { immediate: true });
});
Can someone explain what's the syntax means?
The full syntax of a watch helper is :
watch(param1,param2,param3)
param1 could be a ref data or a getter function that returns another reactive data (prop, computed or reactive ...)
param2 is the callback handler that takes two parameters the new value and the old one
param3 is the watch options like {immediate:true,deep:true} which is optional
That's just another way of using vm.$watch API, please refer to the Vue documentation
The key point to take away here is that you can not only watch a data or a computed property, but also an expression. The expression only accepts dot-delimited paths. For more complex expressions, use a function instead.
This is an example:
vm.$watch(
function () {
// every time the expression `this.a + this.b` yields a different result,
// the handler will be called. It's as if we were watching a computed
// property without defining the computed property itself
return this.a + this.b
},
function (newVal, oldVal) {
// do something
}
)
So in your example above, it can simplify to this piece of code below:
watch('route.name', () => {
console.debug(`MyCoolComponent - watch route.name changed to ${route.name}`);
// Do something here...
// Optionally you can set immediate: true config for the watcher to run on init
//}, { immediate: true });
})

watchEffect with side effect but without infinite loop

what I am trying to do is:
construct an URL based on props
initially and whenever the URL changes, fetch some data
Since this is asynchronous and I also want to indicate loading, I use this construct:
const pageUrl = computed(() => `/api/${props.foo}/${props.bar}`)
const state = reactive({
page: null,
error: null,
loading: false
})
watchEffect(async () => {
state.loading = true
try {
const resp = await axios.get(pageUrl.value)
state.page = resp.data
} catch (err) {
state.error = err
console.log(err)
}
state.loading = false
})
// return page, loading, error for the component to use
The problem is that this seems to run in an infinite loop because in the body, I am not only reacting to the pageUrl, but also to state which itself is modified in the function body.
Alternatively, I can use watch(pageUrl, async pageUrl => { ... }), but this seems only to be triggered when pageUrl changes (in my case: I modify the URL because the props are updated via vue-router, but not when I initially visit the URL).
What should I do here, is my idea of signalling the loading state not appropriate here?
From a logical point of view, the page is a computed value, the only reason I use watch here is that it's asynchronous and might yield an error as well.
Thanks to Husam I got aware of the bug, and it seems like changing a piece of state twice introduces this behaviour - in my case setting loading to true and then again to false.
This behaviour is not apparent in Vue3, and a workaround (and in general maybe the much cleaner method) could be to directly use watch instead of watchEffect.
The source code shows different overloads of the funciton, and there is an options argument that is not directly documented in the Vue3 API. There, I found that my call above needs to read like watch(value, async value => { effect }, { immediate: true }).

How can I implement arrow function in VueJS?

My method in vue looks like this :
methods: {
setDate: async function () {
console.log(this.modal)
}
}
I want to change it to an arrow function. I tried like this :
methods: {
setDate: async () => {
console.log(this.modal)
}
}
There exist error like this :
Cannot read property 'modal' of undefined
How can I solve this problem?
use function directly like
methods: {
async setDate() {
console.log(this.modal)
}
}
You are facing this error because an arrow function wouldn't bind this to the vue instance for which you are defining the method. The same would happen if you were to define computed properties using an arrow function.
Don’t use arrow functions on an instance property or callback e.g.
vm.$watch('a', newVal => this.myMethod())
As arrow functions are bound to the parent context, this will not be the Vue instance as you’d expect and this.myMethod will be undefined.
You can read about it here.
This link https://michaelnthiessen.com/this-is-undefined/ says the following:
"An arrow function uses what is called lexical scoping. We'll get into this more in a bit, but it basically means that the arrow function takes this from it's context.
If you try to access this from inside of an arrow function that's on a Vue component, you'll get an error because this doesn't exist!
So in short, try to avoid using arrow functions on Vue components. It will save you a lot of headaches and confusion."

React Native call this.setState inside function

I'm using a xml parser react-native-xml2js in react native, but this "plugin" uses a specific function for parse the xml, I wasn't found a correct way to use "this" within the function, I've tried using bind() in the callback but doesn't work as expected when using bind it fills my variable moments later after executed, so I don't know how to use it, this is my code:
state = { artcicles: null }
componentDidMount() {
fetch('http://example.com/rss.xml')
.then((response) => response.text())
.then((response) => {
parseString(response, function (err, result) {
this.setState({
articles: JSON.stringify(result.rss.channel[0].item)
})
console.log('RAW: ' + result.rss.channel[0].item);
console.log('THIS: ' + this.state.articles);
}.bind(this));
});
}
When calling this.state.articles in render() at beginning shows null but a second later it fills the articles variable but at that moment the app shows the error when I'm trying to access to the variable.
Any ideas?
Thanks.
I can help you observe something. In React, setState is asynchronous, so the code on the following line after setState will be executed immediately after the setState call is placed in the event loop.
Your true issue is that those console.logs are working perfect, you aren't crazy. They are just being executed before setState has completed.
The secret trick here is that setState accepts a second parameter which is a callback that will be executed after the state is updated. You could place all your following logic inside that statement.
Here is a sampler pack:
this.setState({ dogs: 350 }, () => { console.log('The state has been updated.') })
The second parameter to setState() is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead.
Cite: https://reactjs.org/docs/react-component.html
In yours, it could look like this:
componentDidMount() {
fetch('http://example.com/rss.xml')
.then((response) => response.text())
.then((response) => parseString(response, (err, result) =>
this.setState({
articles: JSON.stringify(result.rss.channel[0].item),
}, () => {
console.log('RAW:', result.rss.channel[0].item)
console.log('THIS:', this.state.articles)
}));
);
}
Checkout the modification I did on the console.log. It too can accept multiple parameters.
A list of JavaScript objects to output. The string representations of each of these objects are appended together in the order listed and output. Please be warned that if you log objects in the latest versions of Chrome and Firefox what you get logged on the console is a reference to the object, which is not necessarily the 'value' of the object at the moment in time you call console.log(), but it is the value of the object at the moment you click it open.
Cite: https://developer.mozilla.org/en-US/docs/Web/API/Console/log
I like that definition additionally because it speaks to the asynchronous nature of live references. One function by itself can be synchronous, but due to the callstack and function queue, you can load up an infinite number of functions into the queue and they will complete in a random order based on how long each one takes to complete because only one passes through the callstack at a time, on the main thread. Of course, it seems random order to us, but it's actually the mathematically exact fastest path through all those functions, assuming they are all deterministic.
Fast forward to the code in your question, setState doesn't care to stop surrounding code from executing unless you explicitly tell it to. That's what the callback is for, if you need to run some bonus code.
While we are talking about setState, I should mention also that you can pass a function to it. Imagine that the second parameter callback is your method of looking into the future after setState. The opposite of that is looking into the past, which is where the functional setState comes in handy by giving you closure around the previous unit of time. The previous state also happens to be the current state of whatever you are updating.
Here is a sampler pack for that:
this.setState((prevState) => {
// hello I like cats.gif
// we could run some fascinating code here
// as long as we return an object
console.log('rad')
return {
articles: [ ...prevState.articles, { new: 1, article: true }],
}
})
It gives you a safe window to guarantee state integrity through your update. I showed that example there as spreading an Array into a new Array and appending it with an object to demonstrate similar to a real scenario you might need to refer to the current state as part of your operations.
In a real scenario, you might sharpen that up to this, which capitalizes on implicit return of an Object literal (requires fat arrow syntax):
this.setState(prevState => ({
articles: [ ...prevState.articles, { new: 1, article: true }],
})
Hopefully that helps us see the climate of what is happening. In React, it is important to undergo a formal change management process, so every time you are getting or setting data, you need to be careful who is reading or writing data and from where, like which functions and which part of the program. React's way of taming JavaScript is to try to force data to always flow unidirectionally, immutably, and deterministic.
It makes things easier to reason about if everything is flowing one way. That only works if you require immutability and prefer a deterministic system. It means most functions are written declaratively, so they declare what the state looks like at the start of a function, do stuff, then declare what the state is at the end of the function.
React makes you think you are writing mostly pure JavaScript, but really it is managing your state using a first in, first out technique to avoid race conditions when perhaps thousands of components are trying to write to the state at the same time. While the user is in the browser rolling their face across the keyboard triggering all kinds of events, and we must not block the main thread or else suffer poor UX.
A formal change management process means there is probably an official pattern that you should use every time you get or set data. Luckily, the patterns are usually what you would do if you were writing pure JavaScript. Reactive programming and immutability help tame the wild asynchronous concurrency gods.
Sorry, we are digressing a bit, but I had to do it for science.
TLDR,
it's very important what you are doing before, during, and after this.setState(). It's a special function, a class method on the Component Class. I hope I have helped us understand a couple of its secrets today.
You were seeking to perform two operations in one setState call. Normally, you only do one which is to set the state :) Your intended usage is fine. We do nest one additional dimension, but it's fine because you are just performing one more operation. I wouldn't recommend it if you were doing a chain of functions in the callback.
Notice the React documentation that states,
Generally we recommend using componentDidUpdate() for such logic instead.
The reason it says that is componentDidUpdate() is listening for state changes, so you can run logic there that is listening for certain conditions and then acting. It saves you from having to care about performing a second operation after setState at the callsite.
Imagine you did this.state.hasFetchedStuff = true inside your componentDidMount() and then had something like this in componentDidUpdate():
componentDidUpdate() {
if (this.state.hasFetchedStuff) {
this.triggerSomething()
}
}
That can free your componentDidMount from having to care about anything after getting the data, which is perhaps good decoupling and a nice separation of concerns.
I converted this post into a Medium article as well and added much more detail: https://medium.com/#agm1984/reacts-setstate-is-a-special-function-and-it-helps-with-asynchronous-concurrency-669eddbe3dd1
In the render() method you can use an inline If:
{this.state.articles && (
// Display articles
)}
When articles stops being null the element right after && will appear and while it's null, no errors will be thrown.
Instead of using null for the initial value you can use an empty array []. That way your articles state variable is never in a consistent state. Also, you can avoid having a manual bind in your callback and use arrow functions instead which will keep the current scope. This way you'll have the correct closure to use this.state.
parseString(response, (err, result) => {
this.setState({
articles: JSON.stringify(result.rss.channel[0].item)
})
});
This answer to some tricky
componentDidMount() {
fetch('http://example.com/rss.xml')
.then((response) => response.text())
.then((response) => {
parseString(response, function (err, result) {
this.setState({
articles: JSON.stringify(result.rss.channel[0].item)
})
console.log('RAW: ' + result.rss.channel[0].item);
setTimeout(() => {
console.log('THIS: ' + this.state.articles);
}, 1000);
}.bind(this));
});
}