Apa penggunaan sintaks fungsional setState dalam komponen fungsional react? [duplikat]
kita berbicara tentang memiliki komponen fungsional useState
Katakanlah
const [age, setAge] = useState(0)
sekarang mari kita katakan saat memperbarui age
saya harus menggunakan yang sebelumnyaage
Dokumen React menyebutkan sesuatu yang disebut FUNCTIONAL UPDATES di mana Anda dapat meneruskan fungsi dan argumennya akan menjadi nilai status sebelumnya, misalnya.
setState((previousAge) => previousAge + 1)
mengapa saya perlu melakukan ini ketika saya bisa melakukannya
setState(previousAge + 1)
apa manfaat menggunakan fungsional setState
,
Saya tahu dalam komponen berbasis kelas ada sesuatu yang disebut pengelompokan pembaruan status secara fungsional, tetapi saya tidak dapat menemukan hal seperti itu dalam dokumentasi komponen fungsional.
Jawaban
Mereka tidak sama, jika pembaruan Anda bergantung pada nilai sebelumnya yang ditemukan di negara bagian, maka Anda harus menggunakan bentuk fungsional. Jika Anda tidak menggunakan formulir fungsional dalam kasus ini, kode Anda akan rusak suatu saat.
Mengapa rusak dan kapan
Komponen fungsional React hanyalah closure, nilai state yang Anda miliki di closure mungkin sudah ketinggalan zaman - apa artinya nilai di dalam closure tidak cocok dengan nilai yang ada dalam status React untuk komponen itu, ini bisa terjadi di kasus berikut:
1- operasi asinkron ( Dalam contoh ini klik tambah lambat, lalu klik beberapa kali pada tombol tambah, nanti Anda akan melihat bahwa status disetel ulang ke apa yang ada di dalam penutupan ketika tombol tambah lambat diklik)
const App = () => {
const [counter, setCounter] = useState(0);
return (
<>
<p>counter {counter} </p>
<button
onClick={() => {
setCounter(counter + 1);
}}
>
immediately add
</button>
<button
onClick={() => {
setTimeout(() => setCounter(counter + 1), 1000);
}}
>
Add
</button>
</>
);
};
2- Saat Anda memanggil fungsi pembaruan beberapa kali dalam penutupan yang sama
const App = () => {
const [counter, setCounter] = useState(0);
return (
<>
<p>counter {counter} </p>
<button
onClick={() => {
setCounter(counter + 1);
setCounter(counter + 1);
}}
>
Add twice
</button>
</>
);
}
Masalah mungkin terjadi tergantung pada seberapa cepat / sering penyetel Anda dipanggil.
Jika Anda menggunakan cara sederhana dengan mendapatkan nilai dari closure, panggilan berikutnya antara dua render mungkin tidak memiliki efek yang diinginkan.
Contoh sederhana:
function App() {
const [counter, setCounter] = useState(0);
const incWithClosure = () => {
setCounter(counter + 1);
};
const incWithUpdate = () => {
setCounter(oldCounter => oldCounter + 1);
};
return (<>
<button onClick={_ => { incWithClosure(); incWithClosure(); }}>
Increment twice using incWithClosure
</button>
<button onClick={_ => { incWithUpdate(); incWithUpdate(); }}>
Increment twice using incWithUpdate
</button>
<p>{counter}</p>
</>);
}
Kedua tombol memanggil salah satu metode kenaikan dua kali. Tapi kami mengamati:
- Tombol pertama hanya akan menambah penghitung sebesar 1
- Tombol kedua akan menambah penghitung sebesar 2, yang mungkin merupakan hasil yang diinginkan.
Kapan ini bisa terjadi?
- Jelas, jika
incWithClosure
dipanggil beberapa kali segera setelah satu sama lain - Jika tugas asinkron terlibat, ini dapat dengan mudah terjadi (lihat di bawah)
- Mungkin, jika React memiliki banyak pekerjaan yang harus dilakukan, algoritme penjadwalannya dapat memutuskan untuk menangani beberapa klik yang sangat cepat menggunakan event handler yang sama.
Contoh dengan pekerjaan asinkron (simulasi memuat sumber daya):
function App() {
const [counter, setCounter] = useState(0);
const incWithClosureDelayed = () => {
setTimeout(() => {
setCounter(counter + 1);
}, 1000);
};
const incWithUpdateDelayed = () => {
setTimeout(() => {
setCounter((oldCounter) => oldCounter + 1);
}, 1000);
};
return (
<>
<button onClick={(_) => incWithClosureDelayed()}>
Increment slowly using incWithClosure
</button>
<button onClick={(_) => incWithUpdateDelayed()}>
Increment slowly using incWithUpdate
</button>
<p>{counter}</p>
</>
);
}
Klik tombol pertama dua kali (dalam satu detik) dan amati bahwa penghitung hanya bertambah 1. Tombol kedua memiliki perilaku yang benar.
Karena jika tidak, Anda akan menemukan suatu saat Anda mendapatkan nilai lama age
. Masalahnya adalah, terkadang apa yang Anda sarankan akan berhasil. Tapi terkadang tidak. Ini mungkin tidak rusak dalam kode Anda saat ini tetapi mungkin rusak dalam kode berbeda yang Anda tulis beberapa minggu yang lalu atau kode Anda saat ini beberapa bulan dari sekarang.
Gejalanya benar-benar aneh. Anda dapat mencetak nilai variabel di dalam komponen jsx menggunakan {x}
sintaks dan kemudian mencetak variabel yang sama menggunakan console.log
setelah merender komponen jsx (bukan sebelumnya) dan menemukan bahwa console.log
nilainya sudah usang - console.log
yang terjadi setelah render entah bagaimana dapat memiliki nilai yang lebih lama daripada render.
Jadi nilai sebenarnya dari variabel status mungkin tidak selalu berfungsi dengan benar dalam kode biasa - mereka hanya dirancang untuk mengembalikan nilai terbaru dalam render. Karena alasan ini, mekanisme callback dalam penyetel status diimplementasikan untuk memungkinkan Anda mendapatkan nilai terbaru dari variabel status dalam kode reguler di luar render.