Vue: how to merge two reactive objects without loosing reactivity

4.2k views Asked by At

Consider this illustrative example:

const useFeatureX = () => {
  return Vue.reactive({
    x1: 2,
    x2: 3
  });
};

const useFeatureY = () => {
  return Vue.reactive({
    y1: 1,
    y2: 2
  });
};

const App = {
  setup() {
    return { ...useFeatureX(), ...useFeatureY() };
  }
};

Vue.createApp(App).mount("#root");
input {
  max-width: 50px;
}
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
  x1 + x2: <input type="number" v-model="x1"/> + <input type="number" v-model="x2"/> = {{ +x1 + +x2 }} <br/>
  y1 + y2: <input type="number" v-model="y1"/> + <input type="number" v-model="y2"/> = {{ +y1 + +y2 }}
</div>

As you can see when you run the snippet, after merging two objects from useFeatureX() and useFeatureY() into one { ...useFeatureX(), ...useFeatureY() }, the app doesn't track updates anymore.

How to merge two reactive objects without loosing reactivity?

1

There are 1 answers

2
str On BEST ANSWER

Object destructuring breaks reactivity.

When we want to use a few properties of the large reactive object, it could be tempting to use ES6 destructuring to get properties we want:

[...]

Unfortunately, with such a destructuring the reactivity for both properties would be lost. For such a case, we need to convert our reactive object to a set of refs. These refs will retain the reactive connection to the source object:

Instead, you can use toRefs to "[convert] a reactive object to a plain object where each property of the resulting object is a ref pointing to the corresponding property of the original object".

const useFeatureX = () => {
  return Vue.reactive({
    x1: 2,
    x2: 3
  });
};

const useFeatureY = () => {
  return Vue.reactive({
    y1: 1,
    y2: 2
  });
};

const App = {
  setup() {
    return { ...Vue.toRefs(useFeatureX()), ...Vue.toRefs(useFeatureY()) };
  }
};

Vue.createApp(App).mount("#root");
input {
  max-width: 50px;
}
<script src="https://unpkg.com/vue@next"></script>
<div id="root">
  x1 + x2: <input type="number" v-model="x1"/> + <input type="number" v-model="x2"/> = {{ +x1 + +x2 }} <br/>
  y1 + y2: <input type="number" v-model="y1"/> + <input type="number" v-model="y2"/> = {{ +y1 + +y2 }}
</div>