Skip to content
This repository was archived by the owner on May 3, 2018. It is now read-only.
This repository was archived by the owner on May 3, 2018. It is now read-only.

How should AJAX requests be handled when a component unmounts? #213

@GirlBossRush

Description

@GirlBossRush

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!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions