首页 > 解决方案 > How to "Avoid mutating a prop directly" in a recursive component

问题描述

I understand the concept: the child works on its own copy of the prop data, and when it's changed it it can $emit that change so the parent can update itself.

However, I'm dealing with a recursive tree structure, e.g. a filesystem, is a good analogy.

[
 { type: 'dir', name: 'music', children: [
    { type: 'dir', name: 'genres', children: [
       { type: 'dir', name: 'triphop', children: [
          { type: 'file', name: 'Portishead' },
          ...
       ]},
     ]}
   ]}
]}

I have a recursive component called Thing that takes a childtree prop and looks a bit like this:

<template>
  <input v-model="tree.name" />
  <thing v-for="childtree in tree.children" :tree="childtree" ></thing>
</template>

Modifying a name is obviously going to modify the prop directly, which is to be avoided, and Vue emits a warning about it.

However, the only way I can see to avoid that would be for each component to do a deep copy of childtree; then $emit a copy of our copy and have a @input (or such) copy it back to the original; all the way up the tree, which would then change the props all the way down the tree causing another deep copy of everything!

That feels like it's going to be really inefficient on a tree of any size.

Is there a better way? I know you can trick Vue into not issuing the error/warning example jsfiddle.

标签: javascriptvue.jsvuejs2vue-component

解决方案


Fix for your problem is passing just event from the child component. https://jsfiddle.net/kv1w72pg/2/

<input @input="(e) => $emit('input', e.target.value)" />

That being said I highly suggest you use other solution such as:

  1. Vuex,
  2. Event bus

It will make code clear and ensure one source of truth.

Edit: In the case of recursive inheritance, using provide inject would be easier: https://jsfiddle.net/s0b9fpr8/4/

This way you provide a function that can change the state of base component to all children components (function must be injected).


推荐阅读