2020-04-23

Svelte Tutorial 7: Lifecycle (English & Indonesia)

English


Lifecycle


Every component has a lifecycle that starts when it is created, and ends when it is destroyed. There are a handful of functions that allow you to run code at key moments during that lifecycle.

onMount


The one you'll use most frequently is onMount, which runs after the component is first rendered to the DOM. We briefly encountered it  when we needed to interact with a <canvas> element after it had been rendered. We'll add an onMount handler that loads some data over the network:

<script>
  import { onMount } from 'svelte';
  let photos = [];
  onMount(async () => {
    const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`);
    photos = await res.json();
  });
</script>

It's recommended to put the fetch in onMount rather than at the top level of the <script> because of server-side rendering (SSR). With the exception of onDestroy, lifecycle functions don't run during SSR, which means we can avoid fetching data that should be loaded lazily once the component has been mounted in the DOM.

Lifecycle functions must be called while the component is initialising so that the callback is bound to the component instance — not (say) in a setTimeout.

If the onMount callback returns a function, that function will be called when the component is destroyed.

onDestroy


To run code when your component is destroyed, use onDestroy. For example, we can add a setInterval function when our component initialises, and clean it up when it's no longer relevant. Doing so prevents memory leaks.

<script>

  import { onDestroy } from 'svelte';
  let seconds = 0;
  const interval = setInterval(() => seconds += 1, 1000);
  onDestroy(() => clearInterval(interval));
</script>

While it's important to call lifecycle functions during the component's initialisation, it doesn't matter where you call them from. So if we wanted, we could abstract the interval logic into a helper function in util.js...

import { onDestroy } from 'svelte';
export function onInterval(callback, milliseconds) {
 const interval = setInterval(callback, milliseconds);
  onDestroy(() => {
  clearInterval(interval);
 });
}

then import it into our component:

<script>
 import { onInterval } from './utils.js';
 let seconds = 0;
 onInterval(() => seconds += 1, 1000);
</script>

beforeUpdate and afterUpdate


The beforeUpdate function schedules work to happen immediately before the DOM has been updated. afterUpdate is its counterpart, used for running code once the DOM is in sync with your data. Together, they're useful for doing things imperatively that are difficult to achieve in a purely state-driven way, like updating the scroll position of an element.

let div;
let autoscroll;
beforeUpdate(() => {
  autoscroll = div && (div.offsetHeight + div.scrollTop) > (div.scrollHeight - 20);
});
afterUpdate(() => {
  if (autoscroll) div.scrollTo(0, div.scrollHeight);
});

Note that beforeUpdate will first run before the component has mounted, so we need to check for the existence of div before reading its properties. See working example here.

Tick


The tick function is unlike other lifecycle functions in that you can call it any time, not just when the component first initialises. It returns a promise that resolves as soon as any pending state changes have been applied to the DOM (or immediately, if there are no pending state changes).

When you invalidate component state in Svelte, it doesn't update the DOM immediately. Instead, it waits until the next microtask to see if there are any other changes that need to be applied, including in other components. Doing so avoids unnecessary work and allows the browser to batch things more effectively.

You can see that behaviour in this example. Select a range of text and hit the tab key. Because the <textarea> value changes, the current selection is cleared and the cursor jumps, annoyingly, to the end. We can fix this by importing tick... ...and running it immediately before we set this.selectionStart and .selectionEnd at the end of handleKeydown:

<script>
  import { tick } from 'svelte';
  let text = `Select some text and hit the tab key to toggle uppercase`;

  async function handleKeydown(event) {
    if (event.which !== 9) return;
    event.preventDefault();

    const { selectionStart, selectionEnd, value } = this;
    const selection = value.slice(selectionStart, selectionEnd);

    const replacement = /[a-z]/.test(selection)
      ? selection.toUpperCase()
      : selection.toLowerCase();

    text = (
      value.slice( 0, 
        selectionStart) + replacement + value.slice(selectionEnd));

    await tick();  // update textarea value first
                   // then put selection in place
    this.selectionStart = selectionStart;
    this.selectionEnd = selectionEnd;
  }
</script>

<style> textarea { width: 100%; height: 200px; } </style>

<textarea value={text} on:keydown={handleKeydown}></textarea>




Indonesia


Lifecycle


Setiap komponen mempunyai lifecycle yang dimulai ketika komponen itu dicreate, dan berakhir ketika komponen didestroy. Ada beberapa fungsi yang berguna untuk memungkinkan kita menjalankan program pada saat tertentu selama lifecycle.

onMount


onMount adalah fungsi yang paling sering digunakan, fungsi ini akan berjalan saat komponen pertama kali dirender ke dalam DOM. Kita sempat menggunakan onMount saat melakukan interaksi dengan elemen  <canvas> sesudah dirender. Kita akan menambahkan handler onMount yang akan melakukan loading data melalui jaringan:

<script>
  import { onMount } from 'svelte';
  let photos = [];
  onMount(async () => {
    const res = await fetch(`https://jsonplaceholder.typicode.com/photos?_limit=20`);
    photos = await res.json();
  });
</script>

Alangkah baiknya jika kita meletakkan fetch di onMount dan bukan di baris atas <script> hal ini dikarenakan proses server-side rendering (SSR). Dengan perkecualian untuk onDestroy, fungsi lifecycle tidak berjalan saat SSR, maka kita bisa menghindari fetch data yang bisa diload belakangan kita komponen sudah termount pada DOM.

Fungsi lifecycle harus dipanggil saat komponen dalam proses pembentukan jadi callback nya akan terikat pada komponen itu  — jangan (misalnya) dalam setTimeout.

Jika callback onMount mereturn suatu fungsi, fungsi tersebut akan dipanggil saat komponen di destroy.

onDestroy


Untuk menjalankan program saat komponen di destroy, gunakan onDestroy. Sebagai contoh, kita bisa menambakan fungsi setInterval ketika komponen dibentuk, dan membersihkan ketika interval sudah tidak lagi dibutuhkan. Hal ini untuk mencegah kehabisan memori.

<script>

  import { onDestroy } from 'svelte';
  let seconds = 0;
  const interval = setInterval(() => seconds += 1, 1000);
  onDestroy(() => clearInterval(interval));
</script>

Meskipun penting untuk memanggil fungsi lifecycle selama masa pembentukan komponen. Tapi tidak masalah kita memanggil dari mana. Jadi jika kita mau, kita bisa melakukan proses logika interval  dalam fungsi helper di dalam util.js...

import { onDestroy } from 'svelte';
export function onInterval(callback, milliseconds) {
 const interval = setInterval(callback, milliseconds);
  onDestroy(() => {
  clearInterval(interval);
 });
}

kemudian diimporkan ke dalam komponen:

<script>
 import { onInterval } from './utils.js';
 let seconds = 0;
 onInterval(() => seconds += 1, 1000);
</script>


beforeUpdate and afterUpdate


Fungsi beforeUpdate menjadwalkan kerja program untuk terjadi segera sebelum DOM nya ter update. afterUpdate adalah kebalikannya, digunakan untuk menjalankan program, setelah DOM sudah tersinkronisasi dengan data dalam program. Bersama-sama, kedua fungsi ini berguna untuk melakukan hal-hal yang susah dilakukan dalam cara yang murni berhubungan dengan state, seperti mengupdate posisi scroll suatu elemen.

let div;
let autoscroll;
beforeUpdate(() => {
  autoscroll = div && (div.offsetHeight + div.scrollTop) > (div.scrollHeight - 20);
});
afterUpdate(() => {
  if (autoscroll) div.scrollTo(0, div.scrollHeight);
});

Jangan lupa bahwa beforeUpdate akan dijalankan duluan sebelum komponen sudah ter mount, jadi kita harus melakukan pengecekan keberadaan div, sebelum mengecek properti nya. Contoh aplikasi yang sudah berjalan bisa dilihat di sini.

Tick


Fungsi tick tidak seperti fungsi lifecycle yang lainnya, dalam hal fungsi ini bisa dipanggil kapan saja, bukan hanya saat komponen pertama terbentuk. Fungsi ini akan mereturn promise yang akan cair saat semua perubahan state yang masih pending sudah diterapkan pada DOM (atau segera, jika tidak ada perubahan state yang pending).

Ketika terjadi invalidasi state komponen, perubahan DOM tidak dilakukan langsung. Melainkan menunggu beberapa perubahan kecil lain untuk melihat apabila ada beberapa perubahan yang bisa dilakukan bersamaan, termasuk perubahan di komponen lain. Hal ini dimaksudkan untuk menghindari pekerjaan tambahan dan juga membantui browser untuk bekerja lebih efektif.

Kita bisa melihat kelakukan itu di contoh ini. Pilih sekumpulan teks dan pencet tombol tab. Karena nilai <textarea> berubah, pilihan teks akan hilang dan kursor akan lompat ke bagian akhir, sangat mengganggu.. Kita bisa memperbaiki ini dengan menggunakan tick... dan langsung menjalankan secara langsung sebelum kita mengatur this.selectionStart dan this.selectionEnd di bagian akhir handleKeydown:

<script>
  import { tick } from 'svelte';
  let text = `Select some text and hit the tab key to toggle uppercase`;

  async function handleKeydown(event) {
    if (event.which !== 9) return;
    event.preventDefault();

    const { selectionStart, selectionEnd, value } = this;
    const selection = value.slice(selectionStart, selectionEnd);

    const replacement = /[a-z]/.test(selection)
      ? selection.toUpperCase()
      : selection.toLowerCase();

    text = (
      value.slice( 0, 
        selectionStart) + replacement + value.slice(selectionEnd));

    await tick();  // tick akan update textarea dulu
                   // baru selection akan diset ulang
    this.selectionStart = selectionStart;
    this.selectionEnd = selectionEnd;
  }
</script>

<style> textarea { width: 100%; height: 200px; } </style>

<textarea value={text} on:keydown={handleKeydown}></textarea>



No comments :

Post a Comment

THINK: is it True? is it Helpful? is it Inspiring? is it Necessary? is it Kind?