State dan Lifecycle
Halaman ini akan mengenalkan tentang konsep dari state dan lifecycle di sebuah komponen React. Anda bisa menemukan referensi detail API komponen disini.
Pertimbangkan contoh detak jam dari salah satu bagian sebelumnya. Di Rendering Element, kita baru saja mempelajari satu cara untuk memperbarui UI. Kita memanggil ReactDOM.render()
untuk mengubah hasil render:
function tick() {
const element = (
<div>
<h1>Halo, dunia!</h1>
<h2>Ini {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render( element, document.getElementById('root') );}
setInterval(tick, 1000);
Di bagian ini, Kita akan belajar cara membuat komponen Clock
benar-benar dapat digunakan kembali dan terenkapsulasi. Ini akan mengatur timer-nya sendiri dan memperbaruinya setiap detik.
Kita dapat memulai dengan mengenkapsulasi bagaimana jam terlihat:
function Clock(props) {
return (
<div> <h1>Halo, dunia!</h1> <h2>Ini {props.date.toLocaleTimeString()}.</h2> </div> );
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />, document.getElementById('root')
);
}
setInterval(tick, 1000);
Namun, ia melewatkan persyaratan penting: faktanya bahwa Clock
mengatur timer dan memperbarui UI setiap detik harus merupakan detail implementasi dari Clock
.
Idealnya kita butuh untuk menulis ini sekali dan Clock
dapat memperbarui dirinya sendiri:
ReactDOM.render(
<Clock />, document.getElementById('root')
);
Untuk mengimplementasikan ini, kita perlu untuk menambahkan “state” ke komponen Clock
.
State mirip dengan props, tetapi bersifat pribadi dan sepenuhnya dikendalikan oleh komponen.
Mengubah Sebuah Fungsi ke Sebuah Kelas
Anda bisa mengubah sebuah function component seperti Clock
ke sebuah kelas dalam lima langkah:
- Buat sebuah kelas ES6, dengan nama yang sama, yang diturunkan dari
React.Component
. - Tambah sebuah method kosong yang bernama
render()
. - Pindahkan isi fungsi ke dalam method
render()
. - Ganti
props
denganthis.props
di dalamrender()
. - Hapus deklarasi fungsi kosong yang tersisa.
class Clock extends React.Component {
render() {
return (
<div>
<h1>Halo, dunia!</h1>
<h2>Ini {this.props.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
Clock
sekarang terdefinisikan sebagai sebuah kelas daripada fungsi
method render
akan dipanggil setiap waktu ketika pembaruan terjadi, tapi selama kita me-render <Clock />
simpul DOM yang sama, hanya satu instansi dari kelas Clock
yang akan digunakan. Ini memungkinkan kita untuk mengunakan fitur tambahan seperti local state dan lifecycle method.
Menambahkan State lokal ke sebuah kelas
Kita akan memindahkan date
dari props ke state dalam tiga langkah:
- Ubah
this.props.date
_ denganthis.state.date
di methodrender()
:
class Clock extends React.Component {
render() {
return (
<div>
<h1>Halo, dunia!</h1>
<h2>Ini {this.state.date.toLocaleTimeString()}.</h2> </div>
);
}
}
- Tambah sebuah konstruktor kelas yang menetapkan nilai awal
this.state
:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()}; }
render() {
return (
<div>
<h1>Halo, dunia!</h1>
<h2>Ini {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
Perhatikan bagaimana kami meneruskan props
ke konstruktor dasar:
constructor(props) {
super(props); this.state = {date: new Date()};
}
Kelas komponen harus selalu memanggil ke konstruktor dasar dengan props
.
- Hapus properti
date
dari elemen<Clock />
:
ReactDOM.render(
<Clock />, document.getElementById('root')
);
Kita nanti akan menambahkan kode timer kembali ke komponen itu sendiri.
Hasilnya akan terlihat seperti ini:
class Clock extends React.Component {
constructor(props) { super(props); this.state = {date: new Date()}; }
render() {
return (
<div>
<h1>Halo, dunia!</h1>
<h2>Ini {this.state.date.toLocaleTimeString()}.</h2> </div>
);
}
}
ReactDOM.render(
<Clock />, document.getElementById('root')
);
Selanjutnya, kami akan membuat Clock
mengatur timer sendiri dan memperbarui dirinya sendiri setiap detik.
Menambah Method Lifecycle ke Kelas
Dalam aplikasi dengan banyak komponen, sangat penting untuk membebaskan sumber daya yang diambil oleh komponen ketika mereka dihancurkan.
Kami ingin mengatur timer setiap kali Clock
di-render di DOM untuk pertama kalinya . Ini disebut “mounting” di React.
Kita juga ingin untuk menghapus timer tersebut setiap kali DOM yang diproduksi oleh Clock
dihapus. Ini disebut “unmounting” di React.
Kita dapat mendeklarasi method spesial di kelas komponen untuk menjalankan beberapa kode ketika sebuah komponen mounts dan unmounts:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() { }
componentWillUnmount() { }
render() {
return (
<div>
<h1>Halo, dunia!</h1>
<h2>Ini {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
Method ini disebut ”lifecycle method“.
Method componentDidMount()
berjalan setelah hasil komponen sudah ter-render di DOM. Ini adalah tempat yang bagus untuk mengatur timer:
componentDidMount() {
this.timerID = setInterval( () => this.tick(), 1000 ); }
Perhatikan bagaimana kami menyimpan ID timer langsung pada this
(this.timerID
).
Ketika this.props
diatur oleh React sendiri dan this.state
punya arti spesial, Anda dapat dengan bebas untuk menambah field tambahan di kelas secara manual jika Anda butuh untuk menyimpan sesuatu yang tidak ikut berpartisipasi di alur data (seperti ID timer).
Kita akan menghapus timer di method lifecycle componentWillUnmount()
:
componentWillUnmount() {
clearInterval(this.timerID); }
Akhirnya, kita akan mengimplementasi sebuah method bernama tick()
yang dijalankan oleh komponen Clock
setiap detik.
Itu akan mengunakan this.setState()
untuk menjadwal pembaruan ke state lokal milik komponen:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() { this.setState({ date: new Date() }); }
render() {
return (
<div>
<h1>Hello, dunia!</h1>
<h2>Ini {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
Sekarang jam akan berdetak setiap waktu.
Mari kita rekap dengan cepat apa yang terjadi dan urutan method yang dipanggil:
- Ketika
<Clock />
diberikan keReactDOM.render()
, React memanggil konstruktor dari komponenClock
. KetikaClock
perlu untuk menampilkan waktu saat ini, dia menginisialisaithis.state
dengan sebuah obyek termasuk waktu saat ini. Kita nantinya akan memperbarui status ini. - React kemudian memanggil method
render()
milik komponenClock
. Beginilah cara React belajar apa yang harusnya ditampilkan di layar. React kemudian memperbarui DOM untuk mencocokan hasil renderClock
. - Ketika hasil
Clock
dimasukkan ke dalam DOM, React memanggil method lifecyclecomponentDidMount()
. Didalamnya, komponenClock
menyuruh browser untuk mengatur sebuah timer untuk memanggil methodtick()
milik komponen sekali per detik. - Setiap detik browser memanggil method
tick()
. Didalamnya, komponenClock
menjadwal pembaruan UI dengan memanggilsetState()
dengan sebuah obyek berisi waktu sekarang. Berkat panggilansetState()
, React mengetahui state sudah berubah, dan memanggil methodrender()
lagi untuk mempelajari apa yang harusnya ada di layar. Kali ini,this.state.date
dirender()
method akan berbeda, dan jadi hasil render akan mencakup waktu yang diperbarui. React telah memperbarui DOM dengan sesuai. - Jika komponen
Clock
dihapus dari DOM, React memanggil method lifecyclecomponentWillUnmount()
jadi timer akan berhenti.
Menggunakan State Dengan Benar
Ada tiga hal yang harus Anda ketahui tentang setState()
.
Jangan mengubah State Secara Langsung
Sebagai contoh, ini tidak akan me-render komponen :
// Salah
this.state.comment = 'Halo';
Sebagai gantinya, gunakan setState()
:
// Benar
this.setState({comment: 'Halo'});
Satu-satunya tempat di mana Anda dapat menetapkan this.state
adalah di konstruktor.
Pembaruan State Mungkin Asynchronous
React dapat mengelompokkan beberapa panggilan setState()
menjadi satu untuk kinerja lebih baik.
Karena this.props
dan this.state
mungkin diperbarui secara asynchronous, Anda seharusnya tidak mengandalkan nilai-nilai tersebut untuk menghitung State berikutnya.
Sebagai contoh, kode ini mungkin gagal untuk memperbarui penghitung:
// Salah
this.setState({
counter: this.state.counter + this.props.increment,
});
Untuk memperbaikinya, pakai bentuk kedua dari setState()
yang menerima fungsi daripada sebuah objek. Fungsi itu akan menerima state sebelumnya sebagai argumen pertama, dan props pada waktu itu pembaruanya di terapkan ke argumen kedua:
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
Kita menggunakan arrow function diatas, tetapi juga bisa menggunakan fungsi biasa:
// Benar
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
Pembaruan State Digabungkan
Ketika Anda memanggil setState()
, React mengabungkan obyek yang Anda siapkan ke state saat ini.
Sebagai contoh, state Anda mungkin menganduk beberapa variabel independen:
constructor(props) {
super(props);
this.state = {
posts: [], comments: [] };
}
Kemudian Anda dapat memperbarui mereka secara terpisah dengan memanggil setState()
yang terpisah:
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts });
});
fetchComments().then(response => {
this.setState({
comments: response.comments });
});
}
Pengabunganya dangkal, jadi this.setState({comments})
meninggalkan this.state.posts
dengan utuh, tetapi sepenuhnya menggantikan this.state.comments
.
Data Mengalir ke Bawah
Baik komponen induk maupun anak tidak tahu apakah komponen tertentu mengandung state atau tidak, dan mereka tidak seharusnya peduli apakah itu didefinisikan sebagai fungsi atau kelas.
Inilah sebabnya mengapa state kadang disebut lokal atau terenkapsulasi. Itu tidak dapat diakses oleh komponen apa pun selain dari yang memiliki dan menetapkannya.
Sebuah komponen dapat memilih untuk menurunkan state sebagai props ke komponen turunannya:
<FormattedDate date={this.state.date} />
Komponen FormattedDate
akan menerima date
props-nya dan tidak akan tahu apakah itu datang dari state milik Clock
, dari props milik Clock
, atau itu diketik dengan tangan:
function FormattedDate(props) {
return <h2>Ini {props.date.toLocaleTimeString()}.</h2>;
}
Ini umumnya memanggil aliran data “atas-bawah” atau “searah”. Semua state selalu dimiliki oleh komponen spesifik, dan semua data atau UI yang berasal dari state tersebut hanya bisa mempengaruhi pohon komponen “di bawah” mereka.
Jika Anda membanyangkan pohon komponen sebagai air terjun dari props, tiap state milik komponen seperti sebuah sumber air yang bergabung dengannya pada titik acak tetapi juga mengalir ke bawah.
Untuk menunjukkan jika semua komponen benar-benar terisolasi, Kita dapat membuat sebuah komponen App
yang me-render tiga <Clock>
:
function App() {
return (
<div>
<Clock /> <Clock /> <Clock /> </div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
Setiap Clock
membuat timer-nya sendiri dan memperbaruinya secara independen.
Di aplikasi React, apakah suatu komponen memiliki state atau tidak itu dianggap sebagai detail implementasi komponen yang dapat berubah dari waktu ke waktu. Anda dapat menggunakan komponen tanpa state di dalam komponen dengan state, dan sebaliknya.