Saat ini saya memiliki komponen reaksi yang terlihat seperti ini:

const GeraCard = (cards, cart = false) => {
    return cards.map((v, i) => {
      return (
        <div key={i} className={styles.card}>
          <div onClick={() => urlRender(v.url)} className={styles.cardContent}>
            <div>
              <span className={styles.cardTitulo}>{v.Nome}</span>
            </div>
            <div>
              <span className={styles.cardData}>{v.Data}</span>
              <span className={styles.cardAtivos}>{v.Ativos} ativo(s)</span>
            </div>
            {cart ? <div>R$ {FormatCapital(v.Capital)}</div> : null}
          </div>
          <span className={styles.trash}>
            <FontAwesomeIcon
              icon={faTrash}
              color={"#3c3c3c77"}
              onClick={(e) => {
                e.persist()
                TrashHandler(v.Nome, e)
              }}
            />
          </span>
        </div>
      );
    });
  };

Berdasarkan susunan kartu, itu membuat sesuatu seperti ini:

Komponen yang Dirender

Setiap kali saya mengklik tombol tempat sampah, saya membuat permintaan ke backend saya, mengedit daftar di database saya dan merender ulang komponen berdasarkan "kartu" yang sekarang diperbarui. Masalahnya adalah ini membutuhkan waktu untuk terjadi, jadi saya ingin cara untuk menghapusnya dari dom secara instan sementara backend saya melakukan tugasnya.

Sesuatu seperti

{show ? renderCompoennt : null}

Saya sudah mencoba menggunakan javascript Vanilla untuk mengambil induk dari tempat sampah, yang akan menjadi kartu yang ingin saya hapus, tetapi hasilnya tidak dapat diprediksi dan juga cukup lambat.

Percobaan terakhir saya adalah ini:

const GeraCard = (cards, cart = false) => {
    return cards.map((v, i) => {
      const [show, setShow] = useState(true);
      return (
        <div key={i}>
          {show ?
            <div className={styles.card}>
              <div onClick={() => urlRender(v.url)} className={styles.cardContent}>
                <div>
                  <span className={styles.cardTitulo}>{v.Nome}</span>
                </div>
                <div>
                  <span className={styles.cardData}>{v.Data}</span>
                  <span className={styles.cardAtivos}>{v.Ativos} ativo(s)</span>
                </div>
                {cart ? <div>R$ {FormatCapital(v.Capital)}</div> : null}
              </div>
              <span className={styles.trash}>
                <FontAwesomeIcon
                  icon={faTrash}
                  color={"#3c3c3c77"}
                  onClick={(e) => {
                    setShow(false);
                    e.persist()
                    TrashHandler(v.Nome, e)
                  }}
                />
              </span>
            </div> :
            null
          }
        </div>
      );
    });
  };

Tetapi reaksi tidak akan membiarkan saya melakukan ini. Meskipun cepat, setiap kali satu item dihapus, reaksi mengeluh bahwa "lebih sedikit kait yang dirender" dan membuat aplikasi mogok.

Adakah yang tahu cara mengatasinya? Terima kasih!

1
Gabriel Lüders 11 Mei 2021, 22:16

2 jawaban

Jawaban Terbaik

Masalahnya adalah pada render pertama Anda memiliki {cards.length} panggilan untuk menghubungkan "useState" dalam GeraCard, tetapi setelah penghapusan satu kartu, Anda akan memiliki {cards.length-1} panggilan untuk mengaitkan "useState". Seperti yang dinyatakan oleh dokumen React:

Jangan panggil Hooks di dalam loop, kondisi, atau fungsi bersarang. Sebagai gantinya, selalu gunakan Hooks di level teratas dari fungsi React Anda. Dengan mengikuti aturan ini, Anda memastikan bahwa Hooks dipanggil dalam urutan yang sama setiap kali komponen dirender. Itulah yang memungkinkan React untuk mempertahankan status Hooks dengan benar antara beberapa panggilan useState dan useEffect.

Anda harus mengekstrak konten panggilan balik peta menjadi komponen yang terpisah.

const GeraCards = (cards, cart = false) => {
    return cards.map((v, i) =>
        <GeraCard card={v} index={i} cart={cart} />
    );
};

const GeraCard = ({ card, index, cart }) => {
    const [show, setShow] = useState(true);
    const v = card;
    return (
        <div key={index}>
            {show ?
                <div className={styles.card}>
                    <div onClick={() => urlRender(v.url)} className={styles.cardContent}>
                        <div>
                            <span className={styles.cardTitulo}>{v.Nome}</span>
                        </div>
                        <div>
                            <span className={styles.cardData}>{v.Data}</span>
                            <span className={styles.cardAtivos}>{v.Ativos} ativo(s)</span>
                        </div>
                        {cart ? <div>R$ {FormatCapital(v.Capital)}</div> : null}
                    </div>
                    <span className={styles.trash}>
                        <FontAwesomeIcon
                            icon={faTrash}
                            color={"#3c3c3c77"}
                            onClick={(e) => {
                                setShow(false);
                                e.persist()
                                TrashHandler(v.Nome, e)
                            }}
                        />
                    </span>
                </div> :
                null
            }
        </div>
    );
}
1
Viktor Lova 11 Mei 2021, 19:29

Anda mencoba melakukan beberapa UI yang optimis, di mana Anda berasumsi bahwa tindakan Anda akan berhasil, dan mencerminkan keadaan yang diharapkan/diasumsikan secara instan, sebelum permintaan ke backend selesai. Ini akan menggantikan beberapa indikator kemajuan/sibuk, seperti pemintal, hingga tindakan selesai dengan server.

Masalah pertama dan masalah langsung dalam kode Anda-- itu melanggar rules of hooks, yang menyatakan bahwa kait hanya dapat digunakan di tingkat atas (tidak pernah di dalam loop, kondisional, dll).

Masalah kedua adalah Anda memanfaatkan Vanilla JS untuk memanipulasi DOM secara langsung; ini umumnya merupakan antipattern dalam kerangka kerja MV*, dan sangat banyak di sini. Sebagai gantinya, saya sarankan melakukan pengelolaan dalam model data Anda; sesuatu seperti ini:

  1. Tulis ulang handler .map Anda untuk mengembalikan null jika kartu memiliki properti deleted.
  2. Saat pengguna mengklik tombol tempat sampah, lakukan dua hal:
    1. Buat permintaan ke backend untuk menghapusnya
    2. Gunakan setState untuk menambahkan properti deleted: true ke kartu yang diklik

Sekarang Anda akan mendapatkan rerender yang akan menghilangkan kartu yang dihapus, dan juga membuat permintaan ke backend, semua di dalam model data React. Pastikan Anda menangani kompleksitas untuk:

1. How to handle the response
1. How to handle an error if the deletion fails at the backend
1. How to manage if a user quickly clicks many cards for deletion before any of the requests can complete.

Semoga berhasil, dan selamat coding!

1
Alexander Nied 11 Mei 2021, 19:31