Bereaksi metode siklus hidup ke hook

Dec 16 2020

Saya memiliki blog sederhana dengan artikel. Dan saya ingin menulis ulang kelas menjadi komponen dan kait fungsional. Sekarang saya mendapatkan logika ini dalam metode siklus hidup untuk halaman saya dengan formulir edit / tambahkan: berfungsi dengan baik.

componentDidUpdate(prevProps, prevState) {
 if (this.props.match.params.id !== prevProps.match.params.id) {
    if (prevProps.match.params.id) {
        this.props.onUnload();
      }

      this.id = this.props.match.params.id;
        if (this.id) {
            return this.props.onLoad(userService.articles.get(this.id));
        }
        this.props.onLoad(null);
   }
   this.isLoading = false;
}

componentDidMount() {
  if (this.id) {
    this.isLoading = true;
    return this.props.onLoad(userService.articles.get(this.id));
  }
  this.isLoading = false;
  this.props.onLoad(null);
}
   
componentWillUnmount() {
   this.props.onUnload();
}
   
shouldComponentUpdate(newProps, newState) {
   if (this.props.match.params.id !== newProps.match.params.id) {
      this.isLoading = true;
   }
   return true;
}

Saya menulis ulang semuanya menjadi kait seperti itu:

//componentDidMount
  useEffect(() => {
    if (id) {
      setIsloading(true);
      return props.onLoad(userService.articles.get(id));
    }
    setIsloading(false);
    props.onLoad(null);
  }, []);

  useEffect(()=> {
      prevId.current = id;
      }, [id]
  );

  //componentDidUpdate
  useEffect(() => {
    //const id = props.match.params.id;
    if (id !== prevId.current) {
      if (prevId.current) {
        props.onUnload();
      }
      if (id) {
        return props.onLoad(userService.articles.get(id));
      }
      props.onLoad(null);
    }
    setIsloading(false);
  });

  //componentWillUnmount
  useEffect(() => {
     return props.onUnload();
  }, []);

  1. Saya mendapat kesalahan - "Terlalu banyak render ulang." di Codesandbox kode lengkap: Codesandbox

Aneh, tapi di localhost tidak ada error "Too many re-render."

  1. Tidak tahu apa yang harus dilakukan dengan metode kelas "shouldComponentUpdate" saya bagaimana menulis ulang ke hook. Mencoba 'memo' tetapi tidak tahu bagaimana menulis dalam kasus ini.

  2. Lagi pula saya melewatkan sesuatu, karena semuanya tidak berfungsi - tidak memperbarui bidang formulir dengan benar.

Jika Anda memiliki pengetahuan yang baik tentang react hooks, tolong bantu, atau berikan beberapa saran - bagaimana cara memperbaikinya?

Jawaban

3 Mhmdrz_A Dec 18 2020 at 21:24

Efek tanpa ketergantungan menyebabkan "Too many re-renders.": ini berjalan setelah setiap render kemudian memanggil setIsLoadinguntuk memperbarui state ( loading) yang akan menyebabkan komponen dirender ulang, yang akan menjalankan effectlagi dan setStateakan dipanggil lagi effectdan seterusnya ...

//componentDidUpdate
  useEffect(() => {
    //const id = props.match.params.id;
    if (id !== prevId.current) {
      if (prevId.current) {
        props.onUnload();
      }
      if (id) {
        return props.onLoad(userService.articles.get(id));
      }
      props.onLoad(null);
    }
    setIsloading(false);
  })

untuk memperbaiki masalah, hapus setIsLoadingdari itu, atau tambahkan IsLoadingsebagai ketergantungan.

//componentDidUpdate
  useEffect(() => {
    ...
    setIsloading(false);
  },[isLoading]);

Anda juga dapat menggabungkan dua efek pemasangan menjadi satu seperti ini (milik Anda juga berfungsi, tetapi saya pikir ini lebih disukai secara gaya):

//componentDidMount
  useEffect(() => {
    if (id) {
      setIsloading(true);
      return props.onLoad(userService.articles.get(id));
    }
    setIsloading(false);
    props.onLoad(null);

    //componentWillUnmount
    return function () {
      props.onUnload()
    }
  }, []);

untuk poin kedua: tentang menulis ulang komponen Anda shouldComponentUpdate; pertama saya harus menunjukkan bahwa keberadaan Anda shouldComponentUpdatetidak masuk akal, karena Anda selalu kembali true, dan Anda hanya menggunakannya untuk memicu loadingkeadaan; dan tidak memenuhi syarat untuk ditulis dengan React.memo (yang ~ setara shouldComponentUpdatedengan komponen dalam kelas); jadi Anda hanya perlu sesuatu untuk dieksekusi pada setiap perubahan props untuk menentukan loadingstatus dan untuk itu Anda dapat menggunakan efek propssebagai ketergantungannya seperti ini:

//manually keeping old props id
const prevPropsId = useRef();

useEffect(() => {
   if (prevPropsId.current !== props.match.params.id) {
      setIsLoading(true);
   }
   prevPropsId.current = props.match.params.id;
 }, [props.match.params.id]);
// this hook only run if `props.match.params.id` change 

berdasarkan kesadaran saya ini akan melakukan pekerjaan, (meskipun kesulitan memahami mengapa Anda menulis logika dengan cara ini di tempat pertama) dan jika tidak berfungsi dengan baik Anda dapat menyesuaikan logika sedikit agar sesuai dengan kebutuhan Anda, Anda mendapatkan ide bagaimana efek perubahan alat peraga bekerja. juga banyak yang perlu Anda tangani typeErrorjika berjaga-jaga id, paramsatau matchtidak ada dan rangkaian opsional dapat menjadi alat yang berguna di sini ->props.match?.params?.id

2 deckele Dec 23 2020 at 23:26

Jika Anda melewatkan pengait siklus hidup yang lama, Anda selalu dapat membuatnya kembali sebagai pengait khusus:

function useComponentDidMount(effect) {
  useEffect(() => {
    effect();
  }, []);
}
function useComponentWillUnmount(effect) {
  useEffect(() => {
    return effect;
  }, []);
}
function useComponentDidUpdate(effect, dependencies) {
  const hasMountedRef = useRef(false);
  const prevDependenciesRef = useRef(null)
  useEffect(() => {
    // don't run on first render, only on subsequent changes
    if (!hasMountedRef.current) {
      hasMountedRef.current = true;
      prevDependenciesRef.current = dependencies;
      return;
    }
    // run effect with previous dependencies, like in componentDidUpdate!
    effect(...prevDependenciesRef.current || []);
    prevDependenciesRef.current = dependencies;
  }, dependencies)
}

Penggunaan (contoh Anda difaktorkan ulang):

  //componentDidMount
  useComponentDidMount(() => {
    if (id) {
      setIsloading(true);
      props.onLoad(userService.articles.get(id));
      return;
    }
    setIsloading(false);
    props.onLoad(null);
  });

  //componentDidUpdate
  useComponentDidUpdate((prevId) => {
    if (prevId) {
      props.onUnload();
    }
    if (id) {
      props.onLoad(userService.articles.get(id));
      return;
    }
    props.onLoad(null);
    setIsloading(false);
  }, [id]);

  //componentWillUnmount
  useComponentWillUnmount(() => {
     props.onUnload();
  });