Apa penggunaan sintaks fungsional setState dalam komponen fungsional react? [duplikat]

Dec 13 2020

kita berbicara tentang memiliki komponen fungsional useState

Katakanlah

const [age, setAge] = useState(0)

sekarang mari kita katakan saat memperbarui agesaya 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

2 ehab Dec 14 2020 at 01:38

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>
   
    </>
  );
}
1 HeroWanders Dec 13 2020 at 23:59

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 incWithClosuredipanggil 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.

slebetman Dec 13 2020 at 23:26

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.lognilainya sudah usang - console.logyang 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.