why call recursive method with changing the this object in Vue, throw Error "RangeError: Maximum call stack size exceeded"

Viewed 295

Update

add online codePen..

https://codepen.io/jiaxi0331/pen/xxVZBMz

Description

failed to call the parent method through call recursive method

Code

export default {
  methods: {
    dispatch(componentName, event, value) {
      if (this.$options.name === componentName) {
        this.$emit(event, value);
      } else {
        const parent = this.$parent || this.$root;
        return this.dispatch.call(parent, componentName, event, value); // Error. call this.dispatch in component.
      }
    }
    // broadcast(componentName, event, value) {}
  },

};

Expect && Detail Error

dispatch.call(parent) -> parent.dispatch.call(parent.parent) -> parent.parent.dispatch...

Detail error: Error in v-on handler: "RangeError: Maximum call stack size exceeded"

Version

vue 2.6.1

Try

function dispatch(componentName, event, value) {
  if (this.$options.name === componentName) {
    this.$emit(event, value);
  } else {
    const parent = this.$parent || this.$root;
    return dispatch.call(parent, componentName, event, value);
  }
}
export default {
  methods: {
    dispatch(componentName, event, value) {
      dispatch.call(this, componentName, event, value);
    }

    // broadcast(componentName, event, value) {}
  }
};

It works, i know the reason is that dispatch.call(this.$parent) change the this object, but why this.dispatch.call(this.$parent) failed?

1 Answers

The issue is here: this.$parent || this.$root. Not having a parent and being at the root are the same thing, so it looks like the function will recurse forever if the component name isn't in the ancestry of the original receiver.

There are really two ending conditions when searching up a tree toward the root, (1) target node is found, or (2) reached the root, so target node wasn't found...

dispatch(componentName, event, value) {
  if (this.$options.name === componentName) {
    this.$emit(event, value);
  } else if (!this.$parent) { // another terminal condition, not found
    this.$emit('not found');  // or whatever, just don't recurse here
  } else {
    return this.dispatch.call(this.$parent, componentName, event, value);
  }
}
Related