image
https://unsplash.com/photos/HrUwIKrmPm0

Reaktywność w Vue, czyli reagowanie na zmiany stanu komponentów jest stosunkowo prosta i nie wymaga głębszych analiz. Niestety zdarzają się wyjątki i nie inaczej jest w tym wypadku, że rwiemy włosy z głowy szukając przyczyny rozwiązania problemu, gdzie wszystko jest niby oczywiste. W tym artykule omawiam nieco dokładniej reaktywność w Vue i pewien skrajny przypadek, o którym warto pamiętać.

Na początek załóżmy, że do obiektu umieszczonego w data chcemy dodać kolejny obiekt w momencie kliknięcia (np. w ten sposób):

 data() {
    return {
      noReactive: {
        first: "test 1"
      },
      reactive: {
        first: "el 1"
      }
    };
  },
 methods: {
   addElementNoReactive() {
      this.noReactive.second = "added no reactive ";

      console.log(this.noReactive);
   }
}

Spodziewanym efektem jest dodanie kolejnego zagnieżdżonego elementu. Przetestujmy to zatem poniżej klikając w zarówno "Add without reactivity" oraz "Add with reactivity" i śledząc konsole i widok.

Jak widać w konsoli, elementy zostały dodane, lecz w pierwszym przypadku widok nie został zaktualizowany.

via GIPHY

Rozwiązanie problemu

Przedstawiony błąd wynika z ograniczeń Javascriptu oraz z faktu, że Vue do śledzenia zmian używa Object.observe (które nie jest już wspierane). Vue nadpisuje gettery i settery używając Object.defineProperty tylko na początku i końcu cyklu życia komponentu, więc nie ma możliwości obserwowania i reagowania na zmiany obiektów, które nie zostały wcześniej zainicjalizowane.

W związku z tym, każda zmienna przechowująca dane naszego komponentu powinna zostać umieszczona w data i zainicjalizowana, nawet jeśli będzie pusta.

Twórcy w celu wymuszenia reaktywności na nowo dodanym elemencie dodali funkcję $set, która wymusza dodanie watchera do nowej zmiennej. Jej użycie wygląda następująco:
Vue.set(object, propertyName, value).

Dlatego we wcześniejszym przykładzie klikając w button "Add with reactivity" wszystko działa tak jest tego oczekiwaliśmy.

Podsumowanie

Omówiony przykład to coś z czego każdy programista Vue powinien sobie zdawać sprawę, jeśli nie chce zmarnować dnia na szukanie buga. Coś co w Reactjs po prostu działa, tutaj może być źródłem frustracji, lecz na ten moment pozostało się z tym tylko pogodzić.

Pocieszający jest fakt, że w Vue 3 ten problem nie powinien występować.