Menggunakan Vue Reactivity pada Komponen React
Tags: react, vue3, vuecompositionapi, vueinreact
Published: 2020-09-11 15:48:17

---
title: Menggunakan Vue Reactivity pada Komponen React
published: true
date: 2020-08-23 02:08:49 UTC
tags: react,vue3,vuecompositionapi,vueinreact
canonical_url: https://medium.com/@armedi.id/menggunakan-vue-reactivity-pada-komponen-react-6411aa5d603c
---

Bagi anda yang berkecimpung di dunia _frontend web development_, pasti sudah tidak asing dengan nama React atau Vue. Dua _library_ yang paling populer saat ini untuk membangun aplikasi web modern.

Para penggemar framework Vue pun pastinya sudah tahu bahwa tidak lama lagi Vue akan merilis versi 3. Versi baru ini tidak hanya menambahkan fitur baru, tapi benar-benar _rewrite_ dari awal (Lebih lanjut: [https://increment.com/frontend/making-vue-3/](https://increment.com/frontend/making-vue-3/)).

Salah satu fitur baru di Vue 3 yang mungkin paling banyak disorot adalah [_Composition API_](https://vue-composition-api-rfc.netlify.app) (Tutorial cara menggunakan _Composition API_ pernah dibuat oleh teman saya Semmi Verian di [video youtube](https://www.youtube.com/watch?v=8vpKtJqdJMI)-nya). Namun hal yang lebih menarik bagi saya pribadi adalah model reaktivitas baru yang sama sekali berbeda dengan versi sebelumnya.

Lebih menariknya lagi, modul reaktivitas ini dibuat pada _package_ terpisah, yang artinya tentu saja bisa kita pakai pada aplikasi lain tanpa harus menggunakan framework Vue.

Nah karena saya sama-sama menyukai React dan Vue, jadi saya memutuskan untuk mencoba modul [@vue/reactivity](https://www.npmjs.com/package/@vue/reactivity) pada aplikasi yang dibuat menggunakan React.

Tujuan akhir dari tulisan ini adalah membahas bagaimana kita bisa **memutasi data** tanpa kehilangan reaktivitas dari React dengan menggunakan _package_ @vue/reactivity. Cara seperti ini bukanlah hal baru, tapi juga digunakan oleh library seperti [mobx-react](https://mobx-react.js.org/) (dengan observable dari mobx).

#### _Mutable VS Immutable_

_Mutability_ adalah perbedaan mendasar antara React dengan Vue. React memilih jalur full _immutable data_, sedangkan Vue memilih jalur _mutable data._

Lalu apa perbedaannya?

Sebelumnya kita bahas dulu sedikit tentang _declarative programming._ Dengan library atau framework modern, _web_ _developer_ tidak lagi perlu memikirkan bagaimana meng-_update_ DOM. Cukup dengan mendeklarasikan apa yang dimau, selebihnya mengenai proses _update_ DOM dan bagaimana terjadinya akan diatur oleh _library_ yang dipakai ([Artikel ini](https://dev.to/tylermcginnis/imperative-vs-declarative-programming-2hff-temp-slug-3677542) menjelaskannya dengan lebih baik).

React atau Vue akan mendeteksi perubahan data, dan kemudian melakukan perubahan yang sesuai pada UI. Dan ini terjadi terus-menerus secara otomatis. Atau dalam kata lain, komponen UI bersifat reaktif terhadap perubahan data.

Nah, di sini lah perbedaan antara _immutability_-nya React dengan _mutability_ Vue. React dengan cara yang sangat sederhana mendeteksi perubahan data menggunakan [Object.is](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is). Itulah mengapa kode di bawah tidak akan berjalan sesuai yang diharapkan.

```js
// in react component

const [name, setName] = useState({first: 'John', last: 'Doe'})

const handleChange = (event) => {
  name.first = event.target.value
}
```

Ketika anda melakukan mutasi terhadap data nameseperti kode diatas, React tidak akan melihat perubahan, sehingga _rerender_ pun tidak akan pernah terjadi. Karena _object_ name masih merupakan _object_ yang sama dengan sebelumnya.

Vue menggunakan cara berbeda yang lebih kompleks untuk mendeteksi perubahan data (lebih jelasnya bisa disimak langsung di [dokumentasi resminya](https://v3.vuejs.org/guide/reactivity.html#how-vue-tracks-these-changes)). Intinya adalah data pada Vue bisa dimutasi, dan dengan cerdas Vue akan mendeteksi perubahan kemudian melakukan update yang sesuai.

#### Let’s start playing

Aplikasi yang akan kita buat adalah aplikasi _counter_ dengan fitur _increment_ dan _decrement_, serta ada satu kolom input teks.

_Scroll ke paling bawah jika anda mau langsung melihat source code dan hasil jadinya_

```bash
npm install @vue/reactivity
```

Pertama kita akan buat sebuah fungsi HOC bernama **setup** yang lebih kurang berfungsi sama dengan setup pada _composition API_ Vue. Penggunaanya akan seperti ini:

```js
import React, { useMemo } from 'react';
import { reactive, ref } from '@vue/reactivity';

const setup = (setupFn) => (Component) => (props) => {
  const setupProps = useMemo(setupFn, []);

  // our little trick here

  return <Component {...props} {...setupProps} />;
};

const App = (props) => {
  // This is the react component
};

export default setup(() => {
  const count = ref(0);
  const name = reactive({ first: '' });

  return { count, name };
})(App);
```

Fungsi setup akan memasukkan _return value_ dari setupFn sebagai props pada komponen. Di sini kita gunakan useMemo agar invokasi hanya terjadi sekali ketika _mounting_ saja.

Nah, sekarang bagaimana caranya untuk “memaksa” komponen agar rerender ketika props ini dimutasi?

@vue/reactivity menyediakan fungsi **effect** yang fungsinya adalah melakukan “sesuatu” (dengan meng-_invoke_ _callback_ yang diberikan) setiap kali value reaktif yang dipakai di dalam fungsi itu dimutasi. Fungsi effect ini akan kita tempatkan di dalam hooks useEffect React. Jangan lupa untuk menghentikan effect ketika komponen di-_unmout_ dengan me-_return_ sebuah fungsi untuk stop.

```javascript
import React, { useMemo } from 'react';
import { reactive, ref, effect, stop } from '@vue/reactivity';

const setup = (setupFn) => (Component) => (props) => {
  const setupProps = useMemo(setupFn, []);

  useEffect(() => {
    const reactiveEffect = effect(() => {
      // here we can do something to trigger rerender
    });

    return () => stop(reactiveEffect);
  }, [setupProps]);

  return <Component {...props} {...setupProps} />;
};
```

Untuk “memaksa” _rerender_ kita akan buat satu state yang _value_-nya akan di-_update_ ketika terjadi mutasi data.

```javascript
const setup = (setupFn) => (Component) => (props) => {
  const setupProps = useMemo(setupFn, []);
  const [, setTick] = useState(0);

  useEffect(() => {
    const reactiveEffect = effect(() => {
      // Only one more thing to do here
      setTick((tick) => tick + 1);
    });

    return () => stop(reactiveEffect);
  }, [setupProps]);

  return <Component {...props} {...setupProps} />;
};
```

Oke, tinggal satu langkah lagi

Sebelumnya saya katakan bahwa fungsi effect akan meng-_invoke callback_ yang diberikan setiap kali _value_ reaktif yang dipakai di dalam fungsi itu dimutasi. Terus bagaimana caranya fungsi ini tau ada mutasi?

Sangat sederhana sekali. Setiap value yang diakses di dalamnya akan secara otomatis di-_track._ Ya, cukup diakses saja!

```javascript
useEffect(() => {
  const reactiveEffect = effect(() => {
    setupProps.count.value;
    setupProps.name.first;
    setTick((tick) => tick + 1);
  });

  return () => stop(reactiveEffect);
}, [setupProps]);
```

Karena di dalam komponen kita akan menggunakan value count dan name.first, dua value ini perlu diakses terlebih dahulu di dalam fungsi effect agar komponen bisa melacak mutasi dan melakukan _rerender_ setiap kali ada perubahan.

Oke, cara diatas sebenarnya sudah “benar”. Tapi akan mengakibatkan error no-unused-expressions pada linter, dan juga membuat fungsi setup hanya bisa dipakai untuk satu komponen ini saja.

Sekarang mari kita generalisasi menjadi fungsi **access** yang secara rekursif akan mengakses setiap properti di dalam object

```javascript
import React, { useState, useEffect, useMemo } from 'react';
import { reactive, ref, isRef, effect, stop } from '@vue/reactivity';

const access = (object) => {
  if (isRef(object)) {
    access(object.value);
  } else if (typeof object === 'object') {
    for (let key of Object.keys(object)) {
      access(object[key]);
    }
  }
};

const setup = (setupFn) => (Component) => (props) => {
  const setupProps = useMemo(setupFn, []);
  const [, setTick] = useState(0);
  useEffect(() => {
    const reactiveEffect = effect(() => {
      access(setupProps);
      setTick((tick) => tick + 1);
    });

    return () => stop(reactiveEffect);
  }, [setupProps]);

  return <Component {...props} {...setupProps} />;
};
```

Akhirnya sekarang di dalam komponen React kita bisa melakukan update data dengan cara memutasinya seperti dibawah ini. Perhatikan onChange pada input dan onClick pada button.

```javascript
function App({ count, name }) {
  return (
    <div>
      <input
        placeholder="Input your name"
        onChange={(e) => (name.first = e.target.value)}
      />
      <div>
        <div>Hi, {name.first}</div>
        <div>{count.value}</div>
        <div>
          <span>
            <button type="button" onClick={() => count.value--}>
              Decrement
            </button>
          </span>
          <span>
            <button type="button" onClick={() => count.value++}>
              Increment
            </button>
          </span>
        </div>
      </div>
    </div>
  );
}
```

Dari sini anda bisa lanjutkan bereksperimen untuk membuat _global state management_ menggunakan modul _reactivity_ dari Vue

_source code_:
{% github armedi/vue-reactivity-in-react-example no-readme %}

_codesandbox_:
{% codesandbox kww7z initialpath=/src/App.jsx %}