Bagaimana cara menggunakan componentWillMount () di React Hooks?

176

Dalam dokumen resmi Bereaksi itu menyebutkan -

Jika Anda terbiasa dengan metode siklus hidup kelas React, Anda dapat menganggap useEffect Hook sebagai componentDidMount, componentDidUpdate, dan componentWillUnmount yang digabungkan.

Pertanyaan saya adalah - bagaimana kita bisa menggunakan componentWillMount()metode siklus hidup dalam sebuah kail?

Abrar
sumber

Jawaban:

340

Anda tidak dapat menggunakan salah satu metode siklus hidup yang ada ( componentDidMount, componentDidUpdate, componentWillUnmountdll) di kail. Mereka hanya dapat digunakan dalam komponen kelas. Dan dengan Hooks Anda hanya dapat menggunakan komponen fungsional. Baris di bawah ini berasal dari dokumen Bereaksi:

Jika Anda akrab dengan Bereaksi metode kelas siklus hidup, Anda bisa memikirkan useEffectHook sebagai componentDidMount, componentDidUpdate, dan componentWillUnmountgabungan.

sarankan adalah, Anda dapat meniru metode siklus hidup ini dari komponen kelas dalam komponen fungsional.

Kode di dalam componentDidMountdijalankan sekali ketika komponen sudah terpasang. useEffectsetara hook untuk perilaku ini

useEffect(() => {
  // Your code here
}, []);

Perhatikan parameter kedua di sini (array kosong). Ini hanya akan berjalan sekali.

Tanpa parameter kedua yang useEffectkait akan dipanggil pada setiap membuat komponen yang bisa berbahaya.

useEffect(() => {
  // Your code here
});

componentWillUnmountdigunakan untuk pembersihan (seperti menghapus pendengar acara, membatalkan timer dll). Katakanlah Anda menambahkan pendengar acara componentDidMountdan menghapusnya seperti di componentWillUnmountbawah ini.

componentDidMount() {
  window.addEventListener('mousemove', () => {})
}

componentWillUnmount() {
  window.removeEventListener('mousemove', () => {})
}

Setara dengan kode di atas adalah sebagai berikut

useEffect(() => {
  window.addEventListener('mousemove', () => {});

  // returned function will be called on component unmount 
  return () => {
    window.removeEventListener('mousemove', () => {})
  }
}, [])
Bhaskar Gyan Vardhan
sumber
184
Penjelasan yang bagus untuk acara Siklus Hidup lainnya, tetapi ini bukan pertanyaan khusus tentang alternatif untuk componentWillMount ().
Shiraz
3
Dalam contoh PanResponder saya telah melihat componentWillMount tampaknya diperlukan, jika tidak Anda mendapatkan panHandlers tidak terdefinisi.
Dror Bar
2
Saya sekarang benar-benar mengerti useEffect()fungsinya, terima kasih.
Iharob Al Asimi
67
Mengapa ini jawaban yang diterima ?? Anda tidak menyebutkan setara kait untukcomponentWillMount
Mykybo
3
@ techexpert Pertanyaan yang diajukan setara dengan componentWillMount, bukan componentWillUnmount. Jawaban ini memang tidak menjawab pertanyaan sama sekali, dan hanya mengulangi apa yang sudah diketahui OP.
JoshuaCWebDeveloper
62

gunakan kait ComponentDidMount

Dalam kebanyakan kasus useComponentDidMountadalah alat untuk digunakan. Ini akan berjalan hanya sekali, setelah komponen telah terpasang (render awal).

 const useComponentDidMount = func => useEffect(func, []);

gunakanComponentWillMount

Penting untuk dicatat bahwa komponen dalam kelas componentWillMountdianggap warisan. Jika Anda memerlukan kode untuk menjalankan hanya sekali sebelum komponen dipasang, Anda dapat menggunakan konstruktor. Lebih lanjut di sini . Karena komponen fungsional tidak memiliki equivelant konstruktor, menggunakan hook untuk menjalankan kode hanya sekali sebelum pemasangan komponen mungkin masuk akal dalam kasus-kasus tertentu. Anda dapat mencapainya dengan kail khusus.

const useComponentWillMount = func => {
  const willMount = useRef(true);

  if (willMount.current) {
    func();
  }

  willMount.current = false;
};

Namun, ada jebakan. Jangan menggunakannya untuk mengatur negara Anda secara tidak sinkron (ex mengikuti permintaan server. Seperti yang Anda harapkan akan memengaruhi rendering awal yang tidak akan terjadi). Kasus-kasus seperti itu harus ditangani useComponentDidMount.

Demo

const Component = (props) => {
  useComponentWillMount(() => console.log("Runs only once before component mounts"));
  useComponentDidMount(() => console.log("Runs only once after component mounts"));
  ...

  return (
    <div>{...}</div>
  );
}

Demo Penuh

Ben Carp
sumber
18
Ini adalah satu-satunya jawaban yang menjawab pertanyaan dan masuk akal. Terima kasih!
chumakoff
3
Satu-satunya masalah dengan itu adalah Anda mendapatkan render tambahan karena pembaruan status yang terlibat. Dengan menggunakan ref, Anda mendapatkan perilaku yang diinginkan tanpa render tambahan: `const useComponentWillMount = func => {const willMount = useRef (true); useEffect (() => {willMount.current = false;}, []); if (willMount.current) {func (); }}; `
remix23
2
Implementasi fungsional componentWillMountberbasis ini useEffectmemiliki dua masalah. Yang pertama adalah bahwa tidak ada siklus hidup pemasangan di komponen fungsional, kedua kait akan berjalan setelah komponen diberikan, sehingga Runs only once before component mountsmenyesatkan. Yang kedua adalah yang componentWillMountdisebut rendering server dan useEffecttidak. Banyak perpustakaan masih mengandalkan UNSAFE_componentWillMountkarena saat ini itu adalah satu-satunya cara untuk memicu efek samping server-side.
Paolo Moretti
2
@ PaoloMoretti, terima kasih. Hook componentWillMount ini, tidak sama persis dengan siklus hidup componentWillMount di komponen kelas. Namun, fungsi yang diteruskan ke sana, akan segera berjalan, hanya saja pertama kali dipanggil. Ini secara praktis berarti akan berjalan sebelum diberikan, dan bahkan sebelum mengembalikan nilai untuk pertama kalinya. Bisakah kita setuju tentang itu? Saya setuju bahwa menggunakan nama componentWillMount tidak ideal, karena nama ini membawa makna tertentu dari versi siklus hidup kelas. Mungkin saya lebih baik menyebutnya "useRunPreMount".
Ben Carp
1
@ PaoloMoretti, saya tidak mengerti. Saya tidak bekerja dengan SSR, tetapi tugas saya adalah pada SSR componentWillMount berjalan dua kali - sekali di server dan sekali di klien. Saya pikir hal yang sama berlaku untuk panggilan balik yang dilewatkan untuk menggunakanComponentDidMount. useComponentDidMount relay pada useEffect untuk menghentikan pemanggilan kembali panggilan. Sampai callback useEffect dijalankan, fungsi komponen akan berjalan dua kali - sekali di server dan sekali di klien. Bukankah begitu?
Ben Carp
53

Menurut reactjs.org, componentWillMount tidak akan didukung di masa depan. https://reactjs.org/docs/react-component.html#unsafe_componentwillmount

Tidak perlu menggunakan componentWillMount.

Jika Anda ingin melakukan sesuatu sebelum komponen dipasang, lakukan saja di constructor ().

Jika Anda ingin melakukan permintaan jaringan, jangan lakukan itu di componentWillMount. Karena melakukan ini akan menyebabkan bug yang tidak terduga.

Permintaan jaringan dapat dilakukan di componentDidMount.

Semoga ini bisa membantu.


diperbarui pada 08/03/2019

Alasan mengapa Anda meminta componentWillMount mungkin karena Anda ingin menginisialisasi keadaan sebelum dirender.

Lakukan saja di useState.

const helloWorld=()=>{
    const [value,setValue]=useState(0) //initialize your state here
    return <p>{value}</p>
}
export default helloWorld;

atau mungkin Anda ingin menjalankan fungsi di componentWillMount, misalnya, jika kode asli Anda terlihat seperti ini:

componentWillMount(){
  console.log('componentWillMount')
}

dengan hook, yang perlu Anda lakukan adalah menghapus metode siklus hidup:

const hookComponent=()=>{
    console.log('componentWillMount')
    return <p>you have transfered componeWillMount from class component into hook </p>
}

Saya hanya ingin menambahkan sesuatu ke jawaban pertama tentang useEffect.

useEffect(()=>{})

useEffect berjalan pada setiap render, itu adalah kombinasi dari componentDidUpdate, componentDidMount, dan ComponentWillUnmount.

 useEffect(()=>{},[])

Jika kita menambahkan array kosong di useEffect, ia berjalan tepat saat komponen di-mount. Itu karena useEffect akan membandingkan array yang Anda berikan padanya. Jadi itu tidak harus menjadi array kosong. Ini bisa menjadi array yang tidak berubah. Misalnya, bisa [1,2,3] atau ['1,2']. useEffect masih hanya berjalan ketika komponen dipasang.

Itu tergantung pada Anda apakah Anda ingin menjalankan hanya sekali atau berjalan setelah setiap render. Tidak berbahaya jika Anda lupa menambahkan array selama Anda tahu apa yang Anda lakukan.

Saya membuat sampel untuk kail. Silakan periksa.

https://codesandbox.io/s/kw6xj153wr


diperbarui pada 21/08/2019

Sudah putih sejak saya menulis jawaban di atas. Ada sesuatu yang saya pikir perlu Anda perhatikan. Saat Anda menggunakan

useEffect(()=>{},[])

Saat bereaksi membandingkan nilai yang Anda berikan ke array [], ia menggunakan Object.is () untuk membandingkan. Jika Anda melewatkan objek ke sana, seperti

useEffect(()=>{},[{name:'Tom'}])

Ini persis sama dengan:

useEffect(()=>{})

Itu akan merender ulang setiap kali karena ketika Object.is () membandingkan suatu objek, itu membandingkan referensi bukan nilai itu sendiri. Itu sama dengan mengapa {} === {} menghasilkan false karena referensi mereka berbeda. Jika Anda masih ingin membandingkan objek itu sendiri bukan referensi, Anda dapat melakukan sesuatu seperti ini:

useEffect(()=>{},[JSON.stringify({name:'Tom'})])
MING WU
sumber
17
dan pertanyaannya adalah bagaimana menerapkannya dengan kaitan
Shubham Khatri
3
tetapi Anda tidak perlu mengimplementasikannya dengan pengait karena itu tidak akan didukung. Tidak perlu belajar bagaimana melakukannya dengan kait.
MING WU
1
Sekarang Anda telah menyebutkan bahwa componentDidMount adalah siklus hidup yang benar untuk digunakan, Anda bisa menambahkan cara menerapkannya dalam jawaban Anda dan kemudian jawaban Anda akan lebih masuk akal daripada jawaban yang diterima
Shubham Khatri
8
Tentunya ini harus menjadi jawaban yang diterima - ini menjelaskan bahwa ComponentWillMount tidak tersedia dalam paradigma hooks. Inisialisasi dalam komponen fungsional disederhanakan - hanya perlu menjadi bagian dari fungsi
Shiraz
1
Bagaimana hal ini sama dengan componentWillMount? Jika Anda memasukkan kode ke dalam komponen fungsional, maka ia akan menjalankan setiap render, tidak hanya saat komponen akan dipasang.
Overcode
13

useLayoutEffectbisa menyelesaikan ini dengan satu set pengamat kosong ( []) jika fungsinya benar-benar mirip dengan componentWillMount- itu akan berjalan sebelum konten pertama sampai ke DOM - meskipun sebenarnya ada dua pembaruan tetapi mereka sinkron sebelum menggambar ke layar.

sebagai contoh:


function MyComponent({ ...andItsProps }) {
     useLayoutEffect(()=> {
          console.log('I am about to render!');
     },[]);

     return (<div>some content</div>);
}

Manfaat dibandingkan useStatedengan penginisialisasi / penyetel atau useEffectmeskipun dapat menghitung render pass, tidak ada render ulang yang sebenarnya ke DOM yang akan dilihat pengguna, dan dijalankan sebelum render pertama yang terlihat, yang tidak berlaku untuk useEffect. Kelemahannya tentu saja sedikit keterlambatan dalam render pertama Anda karena pemeriksaan / pembaruan harus terjadi sebelum mengecat ke layar. Itu benar-benar tergantung pada kasus penggunaan Anda.

Saya pikir secara pribadi, useMemobaik-baik saja dalam beberapa kasus niche di mana Anda perlu melakukan sesuatu yang berat - selama Anda ingat itu adalah pengecualian vs norma.

rob2d
sumber
3
useLayoutEffect adalah cara untuk pergi !!!! Ini menjawab pertanyaan saya mengenai memeriksa apakah pengguna masuk. (Masalahnya adalah, komponen akan dimuat, lalu memeriksa apakah pengguna masuk.) Namun, pertanyaan saya adalah, apakah ini praktik standar? Saya tidak melihat terlalu banyak tempat
Jessica
1
ya itu sangat umum; disebutkan dalam dokumen Bereaksi resmi juga - hanya dalam teks yang lebih kecil karena konsekuensi render DOM ganda untuk menjalankan logika sebelum pengguna memperhatikan.
rob2d
Sebenarnya itu berjalan setelah rendering komponen. Jadi ini sangat berbeda dari componentWillMount.
Jiri Mihal
7

Ini adalah cara bagaimana saya mensimulasikan konstruktor dalam komponen fungsional menggunakan useRefpengait:

function Component(props) {
    const willMount = useRef(true);
    if (willMount.current) {
        console.log('This runs only once before rendering the component.');
        willMount.current = false;        
    }

    return (<h1>Meow world!</h1>);
}

Berikut adalah contoh siklus hidup:

function RenderLog(props) {
    console.log('Render log: ' + props.children);
    return (<>{props.children}</>);
}

function Component(props) {

    console.log('Body');
    const [count, setCount] = useState(0);
    const willMount = useRef(true);

    if (willMount.current) {
        console.log('First time load (it runs only once)');
        setCount(2);
        willMount.current = false;
    } else {
        console.log('Repeated load');
    }

    useEffect(() => {
        console.log('Component did mount (it runs only once)');
        return () => console.log('Component will unmount');
    }, []);

    useEffect(() => {
        console.log('Component did update');
    });

    useEffect(() => {
        console.log('Component will receive props');
    }, [count]);


    return (
        <>
        <h1>{count}</h1>
        <RenderLog>{count}</RenderLog>
        </>
    );
}
[Log] Body
[Log] First time load (it runs only once)
[Log] Body
[Log] Repeated load
[Log] Render log: 2
[Log] Component did mount (it runs only once)
[Log] Component did update
[Log] Component will receive props

Tentu saja komponen Kelas tidak memiliki Bodylangkah, tidak mungkin membuat simulasi 1: 1 karena konsep fungsi dan kelas yang berbeda.

Jiri Mihal
sumber
Saya tidak melihat contoh Anda tetapi cuplikan kode pertama Anda berfungsi untuk saya, Terima kasih!
SAndriy
6

Saya menulis pengait khusus yang akan menjalankan fungsi satu kali sebelum render pertama.

useBeforeFirstRender.js

import { useState, useEffect } from 'react'

export default (fun) => {
  const [hasRendered, setHasRendered] = useState(false)

  useEffect(() => setHasRendered(true), [hasRendered])

  if (!hasRendered) {
    fun()
  }
}

Pemakaian:

import React, { useEffect } from 'react'
import useBeforeFirstRender from '../hooks/useBeforeFirstRender'


export default () => { 
  useBeforeFirstRender(() => {
    console.log('Do stuff here')
  })

  return (
    <div>
      My component
    </div>
  )
}
Sedikit waktu
sumber
3

Ada solusi yang bagus untuk diterapkan componentDidMountdan componentWillUnmountdengan useEffect.

Berdasarkan dokumentasi, useEffectdapat mengembalikan fungsi "pembersihan". fungsi ini tidak akan dipanggil pada useEffectpanggilan pertama , hanya pada panggilan berikutnya.

Oleh karena itu, jika kita menggunakan useEffectpengait tanpa ketergantungan sama sekali, pengait akan dipanggil hanya ketika komponen dipasang dan fungsi "pembersihan" dipanggil saat komponen dilepas.

useEffect(() => {
    console.log('componentDidMount');

    return () => {
        console.log('componentWillUnmount');
    };
}, []);

Panggilan fungsi kembali pembersihan dipanggil hanya ketika komponen dilepas.

Semoga ini membantu.

AfikDeri
sumber
2
Bagaimana ini membantu jika tidak ada hubungannya dengan componentWillMount ? Apakah saya melewatkan sesuatu?
ZenVentzi
Ya, Anda kehilangan fakta bahwa dalam useEffectpanggilan yang sama Anda mendapatkan fungsi yang sama componentWillMountdan componentWillUnmountdengan cara yang baik dan bersih
AfikDeri
Itu tidak benar, useEffecthanya berjalan setelah render sementara componentWillMountberjalan sebelum komponen merender.
Overcode
@ Kode berlebih yang saya bicarakan componentDidMounttidak componentWillMount. Saya melewatkan itu dalam pertanyaan, saya buruk.
AfikDeri
1

Anda bisa meretas kait useMemo untuk meniru acara siklus hidup componentWillMount. Kerjakan saja:

const Component = () => {
   useMemo(() => {
     // componentWillMount events
   },[]);
   useEffect(() => {
     // componentWillMount events
     return () => {
       // componentWillUnmount events
     }
   }, []);
};

Anda perlu menyimpan hook useMemo sebelum apa pun yang berinteraksi dengan negara Anda. Ini bukan seperti yang dimaksudkan tetapi itu bekerja untuk saya untuk semua masalah componentWillMount.

Ini berfungsi karena useMemo tidak perlu benar-benar mengembalikan nilai dan Anda tidak harus benar-benar menggunakannya sebagai apa pun, tetapi karena ia menghafal nilai berdasarkan dependensi yang hanya akan berjalan sekali ("[]") dan itu di atas komponen kami, itu berjalan sekali ketika komponen mount sebelum hal lain.

jpmarks
sumber
0

https://reactjs.org/docs/hooks-reference.html#usememo

Ingatlah bahwa fungsi yang diteruskan ke useMemo berjalan selama rendering. Jangan lakukan apa pun di sana yang biasanya tidak Anda lakukan saat rendering. Misalnya, efek samping termasuk dalam useEffect, bukan useMemo.


sumber
usememo adalah untuk optimasi kinerja. Sebuah kail akan diberikan lagi setelah dipasang jika ada perubahan prop, yang mengalahkan tujuan penulis.
maksimal 54
0

Jawaban Ben Carp sepertinya hanya berlaku bagi saya.

Tetapi karena kita menggunakan cara-cara fungsional, pendekatan lain dapat mengambil manfaat dari penutupan dan HoC:

const InjectWillmount = function(Node, willMountCallback) {
  let isCalled = true;
  return function() {
    if (isCalled) {
      willMountCallback();
      isCalled = false;
    }
    return Node;
  };
};

Kemudian gunakan:

const YourNewComponent = InjectWillmount(<YourComponent />, () => {
  console.log("your pre-mount logic here");
});
Kerem atam
sumber
0

Jawaban singkat untuk pertanyaan awal Anda , bagaimana componentWillMountdapat digunakan dengan React Hooks:

componentWillMountsudah usang dan dianggap sebagai warisan . Bereaksi rekomendasi :

Secara umum, sebaiknya gunakan konstruktor () sebagai ganti untuk inisialisasi keadaan.

Sekarang di Hook Hook Anda temukan, apa yang setara dengan konstruktor kelas untuk komponen fungsi adalah:

constructor: Komponen fungsi tidak memerlukan konstruktor. Anda dapat menginisialisasi keadaan di panggilan useState. Jika menghitung keadaan awal itu mahal, Anda bisa melewati fungsi untuk menggunakanState.

Jadi contoh penggunaan componentWillMountterlihat seperti ini:

const MyComp = () => {
  const [state, setState] = useState(42) // set initial value directly in useState 
  const [state2, setState2] = useState(createInitVal) // call complex computation

  return <div>{state},{state2}</div>
};

const createInitVal = () => { /* ... complex computation or other logic */ return 42; };
ford04
sumber
0

Cukup tambahkan array ketergantungan kosong di useEffect itu akan berfungsi sebagai componentDidMount.

useEffect(() => {
  // Your code here
  console.log("componentDidMount")
}, []);
Coding
sumber
0

Ada trik sederhana untuk mensimulasikan componentDidMountdan componentWillUnmountdengan menggunakan useEffect:

useEffect(() => {
  console.log("componentDidMount");

  return () => {
    console.log("componentWillUnmount");
  };
}, []);
Amerika
sumber