Rumah  >  Artikel  >  hujung hadapan web  >  Reaksi: penutupan basi

Reaksi: penutupan basi

王林
王林asal
2024-08-21 06:19:02450semak imbas

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.

Masalahnya

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.

POC

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:

  • Tunjukkan nilai kaunter
  • Tambahan sebanyak 1 kaunter
  • Mulakan pemasa untuk menambah pembilang sebanyak 1 selepas lima saat.
  • Tambahan sebanyak 10 kaunter

Dalam gambar berikut, ia menunjukkan keadaan UI awal apl, dengan pembilang kepada sifar.

React: stale closure

Kami akan mensimulasikan penutupan kaunter dalam tiga langkah:

  1. Bertambah sebanyak 1 kaunter

React: stale closure

  1. Memulakan pemasa untuk meningkat sebanyak 1 selepas lima saat

React: stale closure

  • Bertambah 10 sebelum tamat masa dicetuskan React: stale closure

Selepas 5 saat, nilai pembilang ialah 2.

React: stale closure

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 (

    Closure demo


    Counter value: {counter}


    Follow the istructions to create a closure of the state variable counter

    1. Set the counter to preferred value
    2. Start a timeout and wait for {timeOutInSeconds} to increment the counter (current value is {counter})
    3. Increment by 10 the counter before the timeout

    { renderLogs() }
    ); } export default App;

    Penyelesaian

    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)
    

    Sumber

    • Dmitri Pavlutin Berhati-hati dengan Penutupan Lapuk apabila Menggunakan Cangkuk Reaksi
    • Imran Abdulmalik Menguasai Penutupan dalam JavaScript: Panduan Komprehensif
    • Skop Leksikal Keyur Paralkar dalam JavaScript – Panduan Permulaan
    • Souvik Paul Stale Penutupan dalam React
    • Soumya Dey Memahami Skop Leksikal & Penutupan dalam JavaScript
    • Limpahan timbunan Subash Mahapatra

    Atas ialah kandungan terperinci Reaksi: penutupan basi. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

    Kenyataan:
    Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn