Belajar dengan membaca secara berurutan

localStorage — Menyimpan Data di Browser

Menyimpan value di browser dengan setItem, getItem, dan removeItem milik localStorage, menyimpan array sebagai string JSON, serta menangani null dan batas kapasitas.

Kalau isi keranjang atau pengaturan tampilan pengguna kamu simpan di variabel, semuanya hilang saat halaman dimuat ulang atau ditutup, dan kembali ke keadaan awal saat halaman dibuka lagi. Sejauh ini, belum ada kode yang bisa menyimpan value agar tetap ada setelah halaman dimuat ulang.

Artikel ini membahas localStorage, yang menyimpan value di browser, dan string JSON yang dipakai untuk menyimpan array dan object.

Value yang Tetap Ada Setelah Halaman Ditutup — setItem dan getItem

Misalkan sebuah situs berita ingin mengingat ukuran huruf yang dipilih pengguna dan memakainya lagi di kunjungan berikutnya. Meskipun disimpan di variabel, seperti const fontSize = "large";, variabel hilang saat halaman dimuat ulang atau ditutup, jadi halaman yang dibuka berikutnya tidak bisa membacanya.

localStorage (tempat penyimpanan pasangan key dan value yang disediakan browser) menyimpan value dengan setItem(key, value), membacanya kembali dengan getItem(key), dan menghapusnya dengan removeItem(key).

Tempat penyimpanan ini dipisahkan per origin (bagian URL sebelum path — https://news.example pada https://news.example/settings), dan halaman-halaman dari origin yang sama membaca dan menulis data yang sama.

// Kalau masih ada value yang disimpan sebelumnya, hapus dulu supaya mulai dari keadaan tanpa data
localStorage.removeItem("fontSize");

// Simpan saat pengguna mengganti ukuran huruf menjadi besar
localStorage.setItem("fontSize", "large");

// Saat halaman dibuka, baca value yang disimpan lalu pakai
const fontSize = localStorage.getItem("fontSize");
console.log(fontSize);                            // large

// Menyimpan dengan key yang sama akan menggantikan value sebelumnya
localStorage.setItem("fontSize", "small");
console.log(localStorage.getItem("fontSize"));    // small
Penyimpanan Terpisah untuk Setiap Origin
Origin https://news.example
  • Halaman dan tab dari origin ini berbagi 1 localStorage
localStorage — pasangan key dan value
  • "fontSize""small" (diganti karena disimpan ke key yang sama)
  • Tetap ada setelah halaman ditutup atau dimuat ulang
Halaman yang sedang terbuka
  • Variabel seperti const fontSize
  • Variabel dibuat ulang saat dimuat ulang
Origin https://shop.example
  • Punya localStorage sendiri
  • Tidak bisa membaca "fontSize" yang disimpan news.example
Variabel dibuat ulang saat halaman dimuat ulang, tapi value di localStorage tetap ada. Halaman dari origin lain tidak bisa membacanya, meskipun key-nya sama.

Kalau kamu membaca dengan getItem saat halaman dibuka dan menulis dengan setItem saat pengguna mengubah pengaturan, halaman tetap tampil dengan ukuran huruf yang sama meskipun dimuat ulang. Value yang disimpan tetap ada sampai kode halaman menghapusnya atau pengguna menghapusnya dari pengaturan browser.

Value yang Disimpan Terbawa ke Eksekusi Latihan Berikutnya

Console latihan menjalankan semua latihan JavaScript di situs ini pada origin yang sama, jadi semuanya berbagi 1 localStorage, dan isinya tetap ada meskipun halaman dibuka ulang. Supaya value yang disimpan di eksekusi sebelumnya tidak tercampur ke hasil kamu, setiap latihan dimulai dengan menghapus key yang dipakainya lewat removeItem.

Di aplikasi peta, simpan jenis peta dan satuan jarak di key yang terpisah. Satuan dari kunjungan sebelumnya sudah tersimpan, dan jenis peta yang dipilih, selectedMapType, sudah dideklarasikan.

① Simpan jenis peta yang dipilih.

② Baca satuan jarak yang tersimpan ke dalam variabel.

③ Timpa satuan jaraknya saja dengan "mile".

④ Tampilkan variabel dari ② dan value kedua key dalam bentuk "km / mile / satellite".

(Kalau berjalan dengan benar, penjelasan akan muncul.)

JavaScript / TypeScript Editor

Jalankan kode untuk melihat output

Menyimpan Array dan Object — JSON.stringify

Sebuah situs pemesanan perjalanan menyimpan hotel-hotel favorit pengguna di dalam array dan ingin menampilkannya lagi di kunjungan berikutnya. Memberikan array langsung ke setItem tidak menimbulkan error, tapi value yang dibaca kembali tidak berisi ID maupun nama hotelnya.

setItem mengubah value yang diberikan menjadi string sebelum menyimpannya. Ubah array dan object menjadi string JSON dengan JSON.stringify sebelum disimpan, lalu kembalikan string yang dibaca menjadi array atau object semula dengan JSON.parse.

// Kalau masih ada value yang disimpan sebelumnya, hapus dulu
localStorage.removeItem("favoriteHotels");
const favoriteHotels = [{ id: "H-101", name: "Hotel Pelabuhan" }, { id: "H-204", name: "Wisma Stasiun" }];

// Kalau diberikan langsung, array diubah menjadi string saat disimpan
localStorage.setItem("favoriteHotels", favoriteHotels);
console.log(localStorage.getItem("favoriteHotels"));   // [object Object],[object Object]

// Ubah menjadi string JSON sebelum disimpan
localStorage.setItem("favoriteHotels", JSON.stringify(favoriteHotels));
const saved = localStorage.getItem("favoriteHotels");
console.log(saved);                                    // [{"id":"H-101","name":"Hotel Pelabuhan"},{"id":"H-204","name":"Wisma Stasiun"}]

// Kembalikan string yang dibaca menjadi array sebelum dipakai
const restored = JSON.parse(saved);
console.log(restored[1].name);                         // Wisma Stasiun
Isinya Hilang Saat Disimpan
Array langsungke setItemTersimpan sebagai[object Object],…Hasil getItem:string yang samaNama hotelsudah hilangJSON.stringify,lalu setItemTersimpan sebagai[{"id":"H-101",…getItem, laluJSON.parserestored[1].name:Wisma Stasiun
Array yang diberikan langsung berubah menjadi string [object Object] saat disimpan. Setelah getItem, array aslinya tidak bisa didapatkan kembali.

Angka dan boolean juga diubah diam-diam tanpa error, jadi kamu baru sadar saat perhitungan atau pengecekan if pada value yang dibaca kembali memberi hasil yang salah. Tabel di bawah menunjukkan string yang disimpan untuk setiap value yang diberikan ke setItem, dan apa yang terjadi saat value itu dibaca kembali.

Value untuk setItemString yang disimpanSaat dibaca kembali
Angka 20"20""20" + 1 = "201" (pakai Number)
Boolean false"false"Truthy di if (pakai JSON.parse)
Array string ["A-1", "B-2"]"A-1,B-2"1 string berkoma, bukan array

Di aplikasi belajar bahasa, tambahkan 1 kata ke daftar kata yang perlu diulang. Daftarnya sudah disimpan di key reviewWords, dan kata yang akan ditambahkan, newWord, sudah dideklarasikan.

① Baca daftar yang tersimpan, lalu tampilkan tipe value-nya.

② Kembalikan value yang dibaca menjadi array, lalu tambahkan newWord ke akhirnya.

③ Simpan lagi array yang sudah diperbarui ke key yang sama.

④ Baca lagi dan kembalikan menjadi array, lalu tampilkan jumlah kata dan word dari kata terakhir.

JavaScript / TypeScript Editor

Jalankan kode untuk melihat output

Membaca Key yang Tidak Tersimpan — null dari getItem

Di situs resep, ID resep yang sudah dilihat disimpan sebagai riwayat, dan halaman menampilkan jumlahnya saat dibuka. Pengunjung yang baru pertama kali datang atau yang sudah menghapus riwayatnya tidak punya data tersimpan. Jadi, kalau kamu membaca length dari hasil JSON.parse dengan anggapan riwayatnya pasti ada, baris itu melempar TypeError.

Kalau getItem diberi key yang tidak tersimpan, yang dikembalikan adalah null, bukan undefined. Dengan menulis JSON.parse(localStorage.getItem(key)) ?? [], array kosong dipakai hanya saat tidak ada data yang tersimpan.

// Baca riwayat. Kalau tidak ada yang tersimpan, pakai array kosong
function loadHistory() {
  return JSON.parse(localStorage.getItem("viewHistory")) ?? [];
}

// Kunjungan pertama — belum ada yang tersimpan, jadi getItem mengembalikan null
localStorage.removeItem("viewHistory");
console.log(localStorage.getItem("viewHistory"));   // null
console.log(loadHistory().length);                  // 0

// Saat riwayat tersimpan — string dikembalikan menjadi array
localStorage.setItem("viewHistory", JSON.stringify(["R-12", "R-30"]));
console.log(loadHistory().length);                  // 2

// Setelah removeItem — kembali menjadi null
localStorage.removeItem("viewHistory");
console.log(loadHistory().length);                  // 0
Tanpa Data Tersimpan, parse Juga Mengembalikan null
Tidak tersimpan(baru/dihapus)Hasil getItem:nullJSON.parse(null)juga null?? jadi [],length 0Tersimpan["R-12","R-30"]Hasil getItem:string JSONJSON.parse jadiarray 2 item?? tidak dipakai;length 2
JSON.parse tidak menganggap null dari getItem sebagai error, dan hasilnya juga null. Dengan menambahkan ?? [], hasilnya bisa diperlakukan sebagai array.

Kalau kode pembacaan dijadikan function, pemanggilnya bisa memperlakukan hasilnya sebagai array tanpa mengecek null, bahkan setelah removeItem dipanggil saat logout. removeItem tidak melempar error meskipun key-nya tidak tersimpan, jadi kamu tidak perlu mengecek ada tidaknya data sebelum menghapus.

Di aplikasi laporan harian, simpan laporan yang belum selesai sebagai draf per tanggal. Hanya draf tanggal 10 yang sudah tersimpan, dan function loadDraft yang masih kosong sudah dideklarasikan.

① Buat loadDraft mengembalikan draf untuk sebuah tanggal, atau string kosong kalau tidak ada.

② Baca key tanggal 11 dengan getItem, lalu tampilkan return value-nya.

③ Pakai loadDraft untuk menampilkan panjang draf tanggal 10 dan tanggal 11.

④ Hapus draf tanggal 10, lalu tampilkan panjangnya dengan loadDraft.

JavaScript / TypeScript Editor

Jalankan kode untuk melihat output

Mengembalikan Keranjang Setelah Dimuat Ulang — Function Simpan dan Muat

Berikutnya, keranjang di toko online harus tetap berisi saat halaman dimuat ulang setelah barang ditambahkan. Menambahkan barang dengan cart.push hanya mengubah variabel, jadi kalau baris penyimpanan terlewat di satu tempat saja, barang itu hilang saat halaman dimuat ulang.

Taruh proses penyimpanan di function saveCart dan proses pemuatan di function loadCart, lalu panggil keduanya tepat setelah keranjang berubah dan saat halaman dibuka. setItem bersifat synchronous (menyelesaikan penyimpanan dulu sebelum lanjut ke baris berikutnya), jadi tidak seperti fetch, value yang disimpan bisa dibaca di baris berikutnya tanpa await.

localStorage.removeItem("cart");   // Kalau masih ada keranjang yang disimpan sebelumnya, hapus
// Simpan keranjang sebagai string JSON. Tidak perlu await
function saveCart(cart) {
  localStorage.setItem("cart", JSON.stringify(cart));
}
// Kembalikan keranjang yang disimpan menjadi array. Kalau tidak ada yang tersimpan, kembalikan keranjang kosong
function loadCart() {
  return JSON.parse(localStorage.getItem("cart")) ?? [];
}

// Muat saat halaman dibuka, lalu simpan setelah menambahkan barang
const cart = loadCart();
cart.push({ id: "K-310", qty: 1 });
saveCart(cart);

// Sebagai pengganti memuat ulang, muat lagi dari string yang disimpan tanpa memakai variabel cart
const reloaded = loadCart();
console.log(reloaded.length, reloaded[0].id);     // 1 K-310
Lupa saveCart, Isinya Tidak Kembali
Tambah K-310dengan pushPanggilsaveCart(cart)Muat ulang, laluloadCart()reloaded.lengthbernilai 1Tambah K-310dengan pushLupa memanggilsaveCartMuat ulang, laluloadCart()reloaded.lengthbernilai 0
Tanpa memanggil saveCart, K-310 yang di-push hanya ada di variabel cart. Setelah dimuat ulang, yang kembali adalah isi yang terakhir disimpan.

Kalau semua proses yang mengubah keranjang dikumpulkan dalam 1 function yang memanggil saveCart di akhirnya, kamu tidak perlu menulis penyimpanan di setiap tempat pemanggilan, dan tidak mungkin lupa. Pemuatan juga sudah dibungkus di loadCart, jadi ?? [] tidak mungkin terlupa di salah satu tempat.

setItem Melempar Exception Saat Penyimpanan Penuh

Di Chrome, 1 origin bisa menyimpan sekitar 5,24 juta karakter, dihitung dari key dan value bersama-sama. Kalau melebihi itu, setItem melempar QuotaExceededError dan value-nya tidak tersimpan. Pembacaan dan penulisan bersifat synchronous, jadi halaman juga berhenti merespons selama value yang besar sedang disimpan. Pakai localStorage untuk value kecil seperti keranjang dan pengaturan.

Di layar inspeksi lini produksi, simpan cacat yang ditemukan beserta jumlahnya untuk setiap nomor part. saveDefects, loadDefects, dan function addDefect yang masih kosong sudah dideklarasikan.

① Di addDefect, baca catatan yang tersimpan, lalu cari catatan dengan nomor part yang sama.

② Kalau ketemu, tambah jumlahnya sebanyak 1; kalau tidak, tambahkan catatan dengan jumlah 1. Lalu simpan.

③ Catat cacat untuk W-12, W-40, dan W-12, sesuai urutan itu.

④ Baca lagi catatannya dengan loadDefects, lalu tampilkan setiap nomor part dalam bentuk "W-40: 1".

JavaScript / TypeScript Editor

Jalankan kode untuk melihat output
QUIZ

Cek Pemahaman

Jawab setiap pertanyaan satu per satu.

Soal 1Apa yang terjadi kalau kamu menjalankan localStorage.getItem("theme") dengan key yang belum pernah disimpan?

Soal 2Setelah localStorage.setItem("darkMode", false), apa yang terjadi kalau value yang dibaca kembali dipakai sebagai kondisi if?

Soal 3Kalau halaman dimuat ulang setelah cart.push(book) tanpa memanggil saveCart, apa yang dikembalikan loadCart()?