Cara Meggunakan Vue Refs Di Vue JS - CRUDPRO

Cara Meggunakan Vue Refs Di Vue JS

Cara Meggunakan Vue Refs Di Vue JS
Catatan: Artikel ini mengasumsikan bahwa teman-teman telah memahami dasar-dasar komponen Vue. Jika belum, silahkan baca dokumentasi komponen Vue terlebih dahulu.

Atribut ref dapat disebut langkah paling akhir yang bisa kita pakai untuk merekayasa DOM bila tidak ada langkah lain. Bahkan juga keterangan atribut ref berada di bagian Tangani Kasus Sangat jarang pada dokumentasi Vue. Di situ disebut jika kita harus menghindar kecurangan komponen DOM langsung.

Maka saya berharap rekan-rekan memikirkannya dengan masak saat sebelum memakai feature ref itu. Tidak berarti kita tidak bisa memakainya, di sini saya cuma mengingatkan supaya kita lebih waspada.

Pembukaan

Kita mempunyai komponen Vue seperti berikut:

<template> 
 <nav>
     <input ref="input">
     <p>{{ value }}</p>
 </nav>
</template>

Komponen itu memiliki input dengan atribut ref dan sebuah tag paragaraf yang berisi binding data.

Sebutkanlah kita ingin membuat input itu jadi konsentrasi melalui JavaScript. Kita bisa melakukan dengan $refs.input.focus().

Masalahnya, kadang kita tidak bisa mengakses property refs yang dimaksud. Karena ref sendiri dibuat hasil dari dari pemanggilan fungsi render. Selanjutnya tentang keterangan ini, teman-teman bisa membacanya di bagian API Ref - Vue.js.

Saat sebelum meneruskan ke sisi seterusnya, silahkan kita segarkan pengetahuan mengenai ref dan $refs terlebih dahulu.

ref ialah atribut HTML atau props dari sebuah elemen. Nilainya bisa menjadi nama rekomendasi. Dan, $refs sebagai property instance. Kita bisa mengakses nama rekomendasi yang sudah kita deklarasikan awalnya menggunakan ref dari property ini.

Menggunakan contoh di atas, kita memberi nilai atribut ref dengan nilai input . Maka kita bisa mengaksesnya dengan cara, $refs.input.

Hal Yang Perlu Dicermati

Kita harus mengetahui di mana dan kapan kita mengakses properti tersebut. Berikut beberapa contoh cara mengakses $refs dari tempat yang berbeda-beda:

Created

<script>
export default {
  data() {
      return {
            value: ''
          }
        },
         created() {
            console.log(this.$refs.input)
            // undefined
            console.log(this.$refs)
            // {input: HTMLInputElement}
        }
    }
</script>

Mengakses $refs.input langsung pada created akan menghasilkan nilai undefined. Sama seperti yang saya deskripsikan di atas, ref dibuat hasil dari panggilan fungsi render. Dan created dipanggil saat sebelum fungsi render.

Tapi, hal aneh terjadi bila kita cuma mencetak nilai $refs. Konsol akan menampilkan property input. Kenapa hal itu bisa terjadi? Dion DiFelice memberi penjelasan ringkas dalam suatu utas StackOverflow, Vue.js refs are undefined, even though this.$refs show they're there:

... is that self.refs is passed by reference. So, by the time you viewed the console, the rest of the data finished loading in a matter of milliseconds. And self.$refs.mapRef is not passed by reference, so it remains undefined, as it was during runtime.

Keterangan itu berlaku bila kita mengakses $refs pada siklus hidup life cycle Vue yang lainnya terkecuali mounted dan beforeDestroy.

Computed

<script>
export default {
  computed: {
    value() {
        return this.$refs.input.value
       }
    }
}
</script>

Mengakses $refs.input.nilai secara langsung dalam computed properti akan menghasilkan galat (error) seperti berikut:

// TypeError: Cannot read property 'value' of undefined

Hal itu karena property input sendiri bernilai undefined, sehingga kita pun tidak segera dapat mengakses nilai dari property value.

Selain itu, galat (error) berikut ada:

// [Vue warn]: Error in render: "TypeError: Cannot read property 'value' of undefined"

Hal itu karena galat (error) awalnya terjadi saat proses pe-render-an komponen. Sehingga computed properti nilai akan berharga undefined.

Watch

<script>
export default
 {  data() {
     return {
           value: ''
         }
     },
      watch: {
          // Tidak terjadi apa-apa
         '$refs.input.value': function (currentValue) {
           this.value = currentValue
    }
  }
}
</script>

Memasang watcher perubahan pada input tidak melakukan apa saja, karena $refs sendiri tidak reaktif. Pada salah satunya sisi Menangani Kasus Sangat jarang, Mengakses Instance Komponen Anak dan Elemen Anak, disebut jika:

$refs hanya diisi setelah komponen telah di-render, dan mereka tidak reaktif.
<script>
export default {
  data() {
      return {
            value: ''
      }
  },
  watch: {
     // Tidak terjadi apa-apa
     '$refs.input': function (currentInput) {
       this.value = currentInput.value
    }
  }
}
</script>

Walaupun kita hanya memasang watcher pada properti $refs.input, hasilnya juga akan sama saja. Karena properti $refs sendiri tidaklah reaktif.

Methods

<script>
export default {
  data() {
      return {
            value: ''
      }
  },
    methods: {
        makeFocus() {
            this.$refs.input.focus()
      }
   }
}
</script>

Mengakses $refs dalam methods cukup susah-susah gampang. Karena hal itu bergantung kapan dan dimana kita memanggil metode itu . Maka kita harus memastikan, kita tidak memanggilnya saat $refs sendiri masih berharga undefined.

Mounted

<script>
export default {
	data() {
	   return {
	     value: ''
	   }
	},
	mounted() {
	  console.log(this.$refs.input)
	  // <input></input>
	  console.log(this.$refs)
	  // {input: HTMLInputElement}
  }
}
</script>

Mungkin lokasi yang paling aman untuk mengakses $refs ialah di mounted. Mengakses $refs.input dan $refs akan menghasilkan nilai yang terdefinisi.

Tapi bila kita ingin menambahkan atribut ref pada komponen, kita harus pastikan komponen itu tidak dimuat secara asinkronus. Meskipun kita mengakses $refs pada mounted, hasilnya akan undefined.

<template>
  <app-nav ref="nav"></app-nav>
</template>

<script>
export default {
  components: {
    AppNav: () => import('./components/AppNav')
  },
  mounted() {
    console.log(this.$refs.nav)
    // undefined
    console.log(this.$refs)
    // {}
  }
}</script>

$nextTick

<template>
	<app-nav ref="nav"></app-nav>
</template>

<script>
export default {
  components: {
      AppNav: () => import('./components/AppNav')
  },
    mounted() {
      this.$nextTick(() => {
        console.log(this.$refs.nav)
        // undefined
        console.log(this.$refs)
        // {}
      })
   }
}
</script>

Terkadang kita menggunakan $nextTick untuk menyelesaikan permasalahan untuk mendapatkan nilai dari suatu objek. Tetapi untuk kasus ini, meskipun kita menggunakan $nextTick secara bertingkat, maka hasilnya tidak akan berhasil. Teman-teman dapat mempelajari lebih lanjut mengenai $nextTick pada bagian Pembaruan Antrian Async - Vue.js.

setTimeout



Menggunakan setTimeout akan menunda eksekusi fungsi callback dalam kurun waktu tertentu. Langkah ini akan bekerja bila kita mengetahui berapakah beberapa waktu yang kita perlukan untuk penundaan eksekusi. Penundaan itu tergantung pada sambungan pengguna. Makin lambat internet, makin bertambah juga waktu menunda yang diperlukan.

Katakanlah langkah di atas tidak ada yang dapat diandalkan . Maka, apa yang bisa kita lakukan untuk mengakses nilai $refs secara aman?

Cara Aman

Saat sebelum meneruskan ulasan, saya ingin bercerita bagaimana saya mendapati jalan keluar dari persoalan itu.

Sekian hari lalu, saya menemukan jika demo interaktif pada artikel "Membuat Sistem Reaktivitas Seperti Vue.js Versi Simpel" baik di bagian 1 dan sisi 2 tidak bekerja. Saya curigai hal itu karena tag yang tidak bisa dilakukan dalam saat production.

Saya mencari tahu langkah lain untuk meng-inject dan jalankan JavaScript sesudah browser (browser) selesai memuat kodenya.

Saya memilih untuk memakai $refs. Masalahnya, komponen demonstrasi interaktif itu termuat secara asinkronus. Hingga saya harus memakai $nextTick atau setTimeout untuk tunda script injection. Sayangnya, kedua cara itu tidak bekerja.

Selanjutnya saya ingat satu hal, saya pernah memakai paket NPM namanya wait-for-expect. Paket itu bermanfaat untuk menunggu Jest expectation bila kita menjalankan code secara asinkronus sampai memperoleh hasil yang sesuai.

Rahasianya ialah paket itu jalankan fungsi callback yang berisi code expectation secara terus-terusan dalam waktu tertentu. Eksekusi itu akan stop saat fungsi callback sudah usai atau mungkin tidak menghasilkan galat (error) apa saja.

Kemungkinan cara itu bisa saya pakai untuk mengakses nilai $refs tak perlu memperoleh nilai undefined.

<template>
  <app-nav ref="nav"></app-nav>
</template>

<script>
export default {
  components: {
    AppNav: () => import('./components/AppNav')
  },
  mounted() {
    const interval = setInterval(() => {
      if (this.$refs.nav) {
         console.log(this.$refs.nav)        // VueComponent{}        console.log(this.$refs)        // {nav: VueComponent}        clearInterval(interval)
       }
    }, 50)
  }
}

Dimulai dari lokasi yang paling aman untuk mendapatkan nilai $refs, kita membuat suatu variabel namanya interval yang direkomendasikan di hasil panggilan fungsi setInterval. Fungsi itu akan menjalankan fungsi callback tiap 50ms.

Kita bisa sesuaikan waktu 50ms itu seperti keinginan kita, tapi di sini saya cuma mengikuti apa yang wait-for-expect kerjakan.

Dalam fungsi callback, kita lakukan pengujian bila $refs.nav sudah ada, kita bisa lakukan suatu hal dengan nilainya. Katakanlah seperti mencetaknya pada konsol browser (browser). Selanjutnya, kita panggil fungsi clearInterval dan masukkan variabel interval untuk bersihkan memory dan hentikan panggilan fungsi callback.

Dengan itu, kita tak perlu mengetahui berapakah lamanya waktu nantikan yang diperlukan sampai nilai $refs.nav ada. Selama panggilan fungsi callback sedang berjalan, akan dilaksanakan pengujian pada nilai itu.

Bila rekan-rekan ingin mengetahui bagaimana implikasi cara itu pada demo interaktif.

Kesimpulan

$refs sebagai salah satunya feature Vue.js yang susah-susah gampang untuk digunakan. Kita harus menggunakannya di saat dan tempat yang akurat. Bahkan juga bila kita sudah melakukan, kadang nilai yang dibuat masih undefined.

Lokasi yang paling aman untuk memperoleh nilai $refs ialah di mounted. Selainnya pada tempat itu, akan riskan hasilkan nilai undefined. Hingga, kita harus waspada saat sebelum menggunakannya.

Untuk menangani permasalahan itu, kita bisa memakai fungsi setInterval yang kita panggil di mounted. Dalam fungsi callback-nya, kita lakukan pengujian apa nilai $refs sudah ada atau memang belum. Apabila sudah, karena itu kita bisa lakukan apa saja dengan nilai itu. Dan yang terpenting ialah menghentikan panggilan fungsi callback memakai fungsi clearInterval.