Rumah > Artikel > hujung hadapan web > Reaksi: penutupan basi
Dalam siaran ini, saya akan menunjukkan cara membuat penutupan dalam apl useState hook React.
Saya tidak akan menerangkan apa itu penutupan, kerana terdapat banyak sumber tentang topik ini dan saya tidak mahu berulang. Saya nasihatkan pembacaan artikel oleh @imranabdulmalik ini.
Ringkasnya, penutupan ialah (dari Mozilla):
...gabungan fungsi yang digabungkan bersama-sama (disertakan) dengan rujukan kepada keadaan sekelilingnya (persekitaran leksikal). Dalam erti kata lain, penutupan memberi anda akses kepada skop fungsi luar daripada fungsi dalaman. Dalam JavaScript, penutupan dibuat setiap kali fungsi dicipta, pada masa penciptaan fungsi.
Sekiranya anda tidak biasa dengan istilah persekitaran leksikal, anda boleh membaca artikel ini oleh @soumyadey atau secara alternatif yang ini.
Dalam aplikasi React, anda boleh membuat penutupan pembolehubah milik keadaan komponen secara tidak sengaja yang dibuat dengan cangkuk useState. Apabila ini berlaku, anda menghadapi masalah penutupan lapuk, iaitu, apabila anda merujuk kepada nilai lama keadaan yang sementara itu ia berubah, jadi ia tidak lebih relevan.
Saya telah mencipta aplikasi Demo React yang matlamat utamanya adalah untuk menambah pembilang (kepunyaan keadaan) yang boleh ditutup dalam penutupan dalam panggilan balik kaedah setTimeout.
Ringkasnya, aplikasi ini boleh:
Dalam gambar berikut, ia menunjukkan keadaan UI awal apl, dengan pembilang kepada sifar.
Kami akan mensimulasikan penutupan kaunter dalam tiga langkah:
Selepas 5 saat, nilai pembilang ialah 2.
Nilai jangkaan kaunter hendaklah 12, tetapi kami mendapat 2.
Sebab mengapa ini berlaku kerana kami telah mencipta penutupan kaunter dalam panggilan balik yang dihantar kepada setTimeout dan apabila tamat masa dicetuskan kami menetapkan kaunter bermula dari nilai lama (iaitu 1).
setTimeout(() => { setLogs((l) => [...l, `You closed counter with value: ${counter}\n and now I'll increment by one. Check the state`]) setTimeoutInProgress(false) setStartTimeout(false) setCounter(counter + 1) setLogs((l) => [...l, `Did you create a closure of counter?`]) }, timeOutInSeconds * 1000);
Mengikuti kod penuh komponen apl.
function App() { const [counter, setCounter] = useState(0) const timeOutInSeconds: number = 5 const [startTimeout, setStartTimeout] = useState (false) const [timeoutInProgress, setTimeoutInProgress] = useState (false) const [logs, setLogs] = useState >([]) useEffect(() => { if (startTimeout && !timeoutInProgress) { setTimeoutInProgress(true) setLogs((l) => [...l, `Timeout scheduled in ${timeOutInSeconds} seconds`]) setTimeout(() => { setLogs((l) => [...l, `You closed counter with value: ${counter}\n and now I'll increment by one. Check the state`]) setTimeoutInProgress(false) setStartTimeout(false) setCounter(counter + 1) setLogs((l) => [...l, `Did you create a closure of counter?`]) }, timeOutInSeconds * 1000); } }, [counter, startTimeout, timeoutInProgress]) function renderLogs(): React.ReactNode { const listItems = logs.map((log, index) => {log} ); return{listItems}
; } function updateCounter(value: number) { setCounter(value) setLogs([...logs, `The value of counter is now ${value}`]) } function reset() { setCounter(0) setLogs(["reset done!"]) } return (); } export default App;Closure demo
Counter value: {counter}
Follow the istructions to create a closure of the state variable counter
- Set the counter to preferred value
- Start a timeout and wait for {timeOutInSeconds} to increment the counter (current value is {counter})
- Increment by 10 the counter before the timeout
{ renderLogs() }
Penyelesaian adalah berdasarkan penggunaan cangkuk useRef yang membolehkan anda merujuk nilai yang tidak diperlukan untuk pemaparan.
Jadi kami menambah pada komponen Apl:
const currentCounter = useRef(counter)
Kemudian kami akan mengubah suai panggilan balik setTimeout seperti ditunjukkan di bawah:
setTimeout(() => { setLogs((l) => [...l, `You closed counter with value: ${currentCounter.current}\n and now I'll increment by one. Check the state`]) setTimeoutInProgress(false) setStartTimeout(false) setCounter(currentCounter.current + 1) setLogs((l) => [...l, `Did you create a closure of counter?`]) }, timeOutInSeconds * 1000);
Panggil balik kami perlu membaca nilai pembilang kerana kami log nilai semasa sebelum menambahnya.
Sekiranya, anda tidak perlu membaca nilai, anda boleh mengelakkan penutupan pembilang hanya menggunakan notasi berfungsi untuk mengemas kini pembilang.
seCounter(c => c + 1)
Atas ialah kandungan terperinci Reaksi: penutupan basi. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!