Kuasai React Suspense dengan pola praktis untuk pengambilan data yang efisien, status pemuatan, dan penanganan eror yang kuat. Bangun pengalaman pengguna yang lebih lancar dan tangguh.
Pola React Suspense: Pengambilan Data dan Error Boundaries
React Suspense adalah fitur canggih yang memungkinkan Anda untuk "menunda" (suspend) rendering komponen saat menunggu operasi asinkron, seperti pengambilan data, selesai. Dipasangkan dengan Error Boundaries, fitur ini menyediakan mekanisme yang kuat untuk menangani status pemuatan dan eror, menghasilkan pengalaman pengguna yang lebih lancar dan tangguh. Artikel ini akan menjelajahi berbagai pola untuk memanfaatkan Suspense dan Error Boundaries secara efektif dalam aplikasi React Anda.
Memahami React Suspense
Pada intinya, Suspense adalah mekanisme yang memungkinkan React menunggu sesuatu sebelum me-render komponen. "Sesuatu" ini biasanya adalah operasi asinkron, seperti mengambil data dari API. Alih-alih menampilkan layar kosong atau status perantara yang berpotensi menyesatkan, Anda dapat menampilkan UI fallback (misalnya, pemintal pemuatan) saat data sedang dimuat.
Manfaat utamanya adalah peningkatan performa yang dirasakan dan pengalaman pengguna yang lebih menyenangkan. Pengguna segera disajikan dengan umpan balik visual yang menunjukkan bahwa sesuatu sedang terjadi, daripada bertanya-tanya apakah aplikasi tersebut macet.
Konsep Utama
- Komponen Suspense: Komponen
<Suspense>membungkus komponen yang mungkin menunda. Komponen ini menerima propfallback, yang menentukan UI yang akan dirender saat komponen yang dibungkus ditunda. - UI Fallback: Ini adalah UI yang ditampilkan saat operasi asinkron sedang berlangsung. Bisa berupa apa saja, mulai dari pemintal pemuatan sederhana hingga animasi yang lebih rumit.
- Integrasi Promise: Suspense bekerja dengan Promise. Ketika sebuah komponen mencoba membaca nilai dari Promise yang belum terselesaikan, React menunda komponen tersebut dan menampilkan UI fallback.
- Sumber Data: Suspense mengandalkan sumber data yang sadar-Suspense (Suspense-aware). Sumber-sumber ini mengekspos API yang memungkinkan React mendeteksi kapan data sedang diambil.
Mengambil Data dengan Suspense
Untuk menggunakan Suspense untuk pengambilan data, Anda memerlukan pustaka pengambilan data yang sadar-Suspense. Berikut adalah pendekatan umum menggunakan fungsi fetchData kustom:
Contoh: Pengambilan Data Sederhana
Pertama, buat fungsi utilitas untuk mengambil data. Fungsi ini harus menangani aspek 'penundaan'. Kita akan membungkus panggilan fetch kita dalam sumber daya kustom untuk menangani status promise dengan benar.
// utils/api.js
const wrapPromise = (promise) => {
let status = 'pending';
let result;
let suspender = promise.then(
(r) => {
status = 'success';
result = r;
},
(e) => {
status = 'error';
result = e;
}
);
return {
read() {
if (status === 'pending') {
throw suspender;
} else if (status === 'error') {
throw result;
}
return result;
},
};
};
const fetchData = (url) => {
const promise = fetch(url)
.then((res) => res.json())
.then((data) => data);
return wrapPromise(promise);
};
export default fetchData;
Sekarang, mari kita buat komponen yang menggunakan Suspense untuk menampilkan data pengguna:
// components/UserProfile.js
import React from 'react';
import fetchData from '../utils/api';
const resource = fetchData('https://jsonplaceholder.typicode.com/users/1');
function UserProfile() {
const user = resource.read();
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
);
}
export default UserProfile;
Terakhir, bungkus komponen UserProfile dengan <Suspense>:
// App.js
import React, { Suspense } from 'react';
import UserProfile from './components/UserProfile';
function App() {
return (
<Suspense fallback={<p>Memuat data pengguna...</p>}>
<UserProfile />
</Suspense>
);
}
export default App;
Dalam contoh ini, komponen UserProfile mencoba membaca data user dari resource. Jika data belum tersedia (Promise masih dalam status pending), komponen akan menunda, dan UI fallback ("Memuat data pengguna...") akan ditampilkan. Setelah data diambil, komponen akan di-render ulang dengan informasi pengguna yang sebenarnya.
Manfaat dari pendekatan ini
- Pengambilan data deklaratif: Komponen mengekspresikan data *apa* yang dibutuhkannya, bukan *bagaimana* cara mengambilnya.
- Manajemen status pemuatan terpusat: Komponen Suspense menangani status pemuatan, menyederhanakan logika komponen.
Error Boundaries untuk Ketahanan
Meskipun Suspense menangani status pemuatan dengan baik, ia tidak secara inheren menangani eror yang mungkin terjadi selama pengambilan data atau rendering komponen. Di situlah Error Boundaries berperan.
Error Boundaries adalah komponen React yang menangkap eror JavaScript di mana saja di dalam pohon komponen turunannya, mencatat eror tersebut, dan menampilkan UI fallback alih-alih merusak seluruh aplikasi. Mereka sangat penting untuk membangun UI yang tangguh yang dapat menangani eror tak terduga dengan baik.
Membuat Error Boundary
Untuk membuat Error Boundary, Anda perlu mendefinisikan komponen kelas yang mengimplementasikan metode siklus hidup static getDerivedStateFromError() dan componentDidCatch().
// components/ErrorBoundary.js
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null
};
}
static getDerivedStateFromError(error) {
// Perbarui state sehingga render berikutnya akan menampilkan UI fallback.
return {
hasError: true,
error: error
};
}
componentDidCatch(error, errorInfo) {
// Anda juga bisa mencatat eror ke layanan pelaporan eror
console.error("Caught error: ", error, errorInfo);
this.setState({errorInfo: errorInfo});
}
render() {
if (this.state.hasError) {
// Anda dapat me-render UI fallback kustom apa pun
return (
<div>
<h2>Terjadi kesalahan.</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error && this.state.error.toString()}<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
Metode getDerivedStateFromError dipanggil ketika sebuah eror dilemparkan di komponen turunan. Metode ini memperbarui state untuk menunjukkan bahwa telah terjadi eror.
Metode componentDidCatch dipanggil setelah eror dilemparkan. Metode ini menerima informasi eror dan info eror, yang dapat Anda gunakan untuk mencatat eror ke layanan pelaporan eror atau menampilkan pesan eror yang lebih informatif.
Menggunakan Error Boundaries dengan Suspense
Untuk menggabungkan Error Boundaries dengan Suspense, cukup bungkus komponen <Suspense> dengan komponen <ErrorBoundary>:
// App.js
import React, { Suspense } from 'react';
import UserProfile from './components/UserProfile';
import ErrorBoundary from './components/ErrorBoundary';
function App() {
return (
<ErrorBoundary>
<Suspense fallback={<p>Memuat data pengguna...</p>}>
<UserProfile />
</Suspense>
</ErrorBoundary>
);
}
export default App;
Sekarang, jika terjadi eror selama pengambilan data atau rendering komponen UserProfile, Error Boundary akan menangkap eror tersebut dan menampilkan UI fallback, mencegah seluruh aplikasi mengalami crash.
Pola Suspense Tingkat Lanjut
Selain pengambilan data dasar dan penanganan eror, Suspense menawarkan beberapa pola tingkat lanjut untuk membangun UI yang lebih canggih.
Code Splitting dengan Suspense
Code splitting adalah proses memecah aplikasi Anda menjadi potongan-potongan yang lebih kecil yang dapat dimuat sesuai permintaan. Ini dapat secara signifikan meningkatkan waktu muat awal aplikasi Anda.
React.lazy dan Suspense membuat code splitting menjadi sangat mudah. Anda dapat menggunakan React.lazy untuk mengimpor komponen secara dinamis, lalu membungkusnya dengan <Suspense> untuk menampilkan UI fallback saat komponen sedang dimuat.
// components/MyComponent.js
import React from 'react';
const MyComponent = React.lazy(() => import('./AnotherComponent'));
function App() {
return (
<Suspense fallback={<p>Memuat komponen...</p>}>
<MyComponent />
</Suspense>
);
}
export default App;
Dalam contoh ini, MyComponent dimuat sesuai permintaan. Saat sedang dimuat, UI fallback ("Memuat komponen...") ditampilkan. Setelah komponen dimuat, komponen tersebut dirender secara normal.
Pengambilan Data Paralel
Suspense memungkinkan Anda untuk mengambil beberapa sumber data secara paralel dan menampilkan satu UI fallback saat semua data sedang dimuat. Ini bisa berguna ketika Anda perlu mengambil data dari beberapa API untuk me-render satu komponen.
import React, { Suspense } from 'react';
import fetchData from './api';
const userResource = fetchData('https://jsonplaceholder.typicode.com/users/1');
const postsResource = fetchData('https://jsonplaceholder.typicode.com/posts?userId=1');
function UserProfile() {
const user = userResource.read();
const posts = postsResource.read();
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<h3>Postingan:</h3>
<ul>
{posts.map(post => (<li key={post.id}>{post.title}</li>))}
</ul>
</div>
);
}
function App() {
return (
<Suspense fallback={<p>Memuat data pengguna dan postingan...</p>}>
<UserProfile />
</Suspense>
);
}
export default App;
Dalam contoh ini, komponen UserProfile mengambil data pengguna dan data post secara paralel. Komponen <Suspense> menampilkan satu UI fallback saat kedua sumber data sedang dimuat.
API Transisi dengan useTransition
React 18 memperkenalkan hook useTransition, yang menyempurnakan Suspense dengan menyediakan cara untuk mengelola pembaruan UI sebagai transisi. Ini berarti Anda dapat menandai pembaruan state tertentu sebagai kurang mendesak dan mencegahnya memblokir UI. Ini sangat membantu ketika berhadapan dengan pengambilan data yang lebih lambat atau operasi rendering yang kompleks, sehingga meningkatkan performa yang dirasakan.
Berikut cara menggunakan useTransition:
import React, { useState, Suspense, useTransition } from 'react';
import fetchData from './api';
const resource = fetchData('https://jsonplaceholder.typicode.com/users/1');
function UserProfile() {
const user = resource.read();
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
</div>
);
}
function App() {
const [isPending, startTransition] = useTransition();
const [showProfile, setShowProfile] = useState(false);
const handleClick = () => {
startTransition(() => {
setShowProfile(true);
});
};
return (
<div>
<button onClick={handleClick} disabled={isPending}>
Tampilkan Profil Pengguna
</button>
{isPending && <p>Memuat...</p>}
<Suspense fallback={<p>Memuat data pengguna...</p>}>
{showProfile && <UserProfile />}
</Suspense>
</div>
);
}
export default App;
Dalam contoh ini, mengklik tombol "Tampilkan Profil Pengguna" akan memulai transisi. startTransition menandai pembaruan setShowProfile sebagai transisi, memungkinkan React untuk memprioritaskan pembaruan UI lainnya. Nilai isPending dari useTransition menunjukkan apakah transisi sedang berlangsung, memungkinkan Anda untuk memberikan umpan balik visual (misalnya, menonaktifkan tombol dan menampilkan pesan pemuatan).
Praktik Terbaik Menggunakan Suspense dan Error Boundaries
- Bungkus Suspense di area sekecil mungkin: Hindari membungkus bagian besar aplikasi Anda dengan
<Suspense>. Sebaliknya, bungkus hanya komponen yang benar-benar perlu ditunda. Ini akan meminimalkan dampak pada sisa UI. - Gunakan UI fallback yang bermakna: UI fallback harus memberikan umpan balik yang jelas dan informatif kepada pengguna tentang apa yang sedang terjadi. Hindari pemintal pemuatan generik; sebaliknya, coba berikan lebih banyak konteks (misalnya, "Memuat data pengguna...").
- Tempatkan Error Boundaries secara strategis: Pikirkan baik-baik di mana menempatkan Error Boundaries. Tempatkan cukup tinggi di pohon komponen untuk menangkap eror yang mungkin memengaruhi beberapa komponen, tetapi cukup rendah untuk menghindari penangkapan eror yang spesifik untuk satu komponen.
- Catat eror: Gunakan metode
componentDidCatchuntuk mencatat eror ke layanan pelaporan eror. Ini akan membantu Anda mengidentifikasi dan memperbaiki eror di aplikasi Anda. - Sediakan pesan eror yang ramah pengguna: UI fallback yang ditampilkan oleh Error Boundaries harus memberikan informasi yang bermanfaat kepada pengguna tentang eror dan apa yang dapat mereka lakukan. Hindari jargon teknis; sebaliknya, gunakan bahasa yang jelas dan ringkas.
- Uji Error Boundaries Anda: Pastikan Error Boundaries Anda berfungsi dengan benar dengan sengaja melemparkan eror di aplikasi Anda.
Pertimbangan Internasional
Saat menggunakan Suspense dan Error Boundaries dalam aplikasi internasional, pertimbangkan hal berikut:
- Lokalisasi: Pastikan UI fallback dan pesan eror dilokalkan dengan benar untuk setiap bahasa yang didukung oleh aplikasi Anda. Gunakan pustaka internasionalisasi (i18n) seperti
react-intlataui18nextuntuk mengelola terjemahan. - Tata letak Kanan-ke-Kiri (RTL): Jika aplikasi Anda mendukung bahasa RTL (misalnya, Arab, Ibrani), pastikan UI fallback dan pesan eror ditampilkan dengan benar dalam tata letak RTL. Gunakan properti logis CSS (misalnya,
margin-inline-startalih-alihmargin-left) untuk mendukung tata letak LTR dan RTL. - Aksesibilitas: Pastikan UI fallback dan pesan eror dapat diakses oleh pengguna dengan disabilitas. Gunakan atribut ARIA untuk memberikan informasi semantik tentang status pemuatan dan pesan eror.
- Sensitivitas budaya: Perhatikan perbedaan budaya saat merancang UI fallback dan pesan eror. Hindari penggunaan gambar atau bahasa yang mungkin menyinggung atau tidak pantas di budaya tertentu. Misalnya, pemintal pemuatan yang umum mungkin dianggap negatif di beberapa budaya.
Contoh: Pesan Eror yang Dilokalkan
Menggunakan react-intl, Anda dapat membuat pesan eror yang dilokalkan:
// components/ErrorBoundary.js
import React from 'react';
import { FormattedMessage } from 'react-intl';
class ErrorBoundary extends React.Component {
// ... (sama seperti sebelumnya)
render() {
if (this.state.hasError) {
return (
<div>
<h2><FormattedMessage id="error.title" defaultMessage="Terjadi kesalahan." /></h2>
<p><FormattedMessage id="error.message" defaultMessage="Silakan coba lagi nanti." /></p>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
Kemudian, definisikan terjemahan di file lokal Anda:
// locales/en.json
{
"error.title": "Terjadi kesalahan.",
"error.message": "Silakan coba lagi nanti."
}
// locales/fr.json
{
"error.title": "Terjadi kesalahan.",
"error.message": "Silakan coba lagi nanti."
}
Kesimpulan
React Suspense dan Error Boundaries adalah alat penting untuk membangun UI yang modern, tangguh, dan ramah pengguna. Dengan memahami dan menerapkan pola yang dijelaskan dalam artikel ini, Anda dapat secara signifikan meningkatkan performa yang dirasakan dan kualitas keseluruhan aplikasi React Anda. Ingatlah untuk mempertimbangkan internasionalisasi dan aksesibilitas untuk memastikan bahwa aplikasi Anda dapat digunakan oleh audiens global.
Pengambilan data asinkron dan penanganan eror yang tepat adalah aspek penting dari aplikasi web mana pun. Suspense, yang digabungkan dengan Error Boundaries, menawarkan cara yang deklaratif dan efisien untuk mengelola kompleksitas ini di React, menghasilkan pengalaman pengguna yang lebih lancar dan andal bagi pengguna di seluruh dunia.