-
Notifications
You must be signed in to change notification settings - Fork 11
How should AJAX requests be handled when a component unmounts? #213
Description
Consider the following, a two page application featuring "home" and "profile". The profile page requires an AJAX request to populate the template.
http://jsfiddle.net/nps44dk9/2/
If the user navigates to the profile page and navigates back to the homepage before the AJAX request completes, this._data
is undefined
and an error will occur. The profile page component is unmounted before the request completes.
In this small example the profile
property could instead be placed on the root
data instead. But what design is appropriate for a larger application? More complex components like forms that perform server-side validation make storing everything on root
difficult.
var ValidatedField = {
computed: {
validText: function () {
return this.isValid ? "Valid" : "Invalid";
}
},
data: function () {
return {
isValid: true,
value: ""
};
},
methods: {
validate: function () {
// Perhaps checking if a username is available...
$.ajax("/example/foo/bar", {
success: function (response) {
// This will error if the user navigates away from the form
// before the response has finished.
this.isValid = response;
}.bind(this)
});
}
},
template: "<input v-on='change: validate' v-attr='value: value' /> {{validText}}"
};
My team has had some success by monitoring a component's this._data
, this._isBeingDestroyed
, and this._isDestroyed
properties.
// Wraps jQuery-esq AJAX library, preventing late requests from running on unmounted components.
function VueAJAXWrapper (path, options) {
var componentContext = this;
$.ajax(path, {
data: options.data,
error: function (response) {
if (!componentContext._isBeingDestroyed && !componentContext._isDestroyed) {
options.error.call(componentContext, response);
}
},
success: function (response) {
if (!componentContext._isBeingDestroyed && !componentContext._isDestroyed) {
options.success.call(componentContext, response);
}
},
type: options.type
})
}
var SettingsPage = {
created: function () {
// Fetch initial profile.
// this.profile = {...};
},
methods: {
updateProfile: function () {
// Must be called with component context to know if component is still mounted.
VueAJAXWrapper.call(this, "user/foobar/edit", {
type: 'post',
error: function (response) {
console.error(response);
},
success: function (response) {
this.profile = response;
}
});
}
},
data: function () {
return {
profile: null
};
},
template: "..."
};
I'm concerned this approach relies too much on component context. Accessing private component properties seems dangerous without @yyx990803's blessing. Does anyone have any feedback on how they would approach this?
Thank you!