Cara Menggunakan useEffect dan useState dengan Betul dalam React

Kemaskini terakhir: 02/12/2026
Pengarang C SourceTrail
  • Fahami bagaimana useState memelihara dan mengemas kini keadaan komponen setempat, termasuk kemas kini fungsi dan pengendalian objek.
  • Gunakan useEffect untuk kesan sampingan dengan logik persediaan/pembersihan yang jelas dan tatasusunan kebergantungan yang tepat untuk mengelakkan kebocoran dan gelung.
  • Gabungkan useState dan useEffect untuk tugasan dunia sebenar seperti pengambilan data, langganan dan kemas kini DOM dalam komponen fungsi.
  • Ikut peraturan cangkuk dan layan kesan sebagai proses "selepas pemaparan" untuk memastikan komponen React boleh diramal dan diselenggara.

Cangkuk reaksi useState dan useEffect

Cangkuk React telah mengubah sepenuhnya cara kita menulis komponen, dan menguasai useState and useEffect pada dasarnya merupakan tiket masuk untuk menulis kod React moden. Jika anda sudah menggunakannya tetapi masih tersekat dengan gelung tak terhingga, keadaan lapuk atau tatasusunan kebergantungan yang mengelirukan, panduan ini akan membantu anda menghubungkan semua bahagian yang hilang secara praktikal.

Dalam artikel ini, kita akan membincangkan secara mendalam cara penggunaan yang betul useState and useEffect bersama-sama, mengapa cangkuk diperkenalkan pada mulanya, peraturan dan kaveat rasmi, bagaimana kebergantungan benar-benar berfungsi secara tersembunyi, perangkap biasa yang merosakkan komponen anda dan corak yang telah diuji untuk kesan sampingan, pembersihan dan pengurusan keadaan dalam projek sebenar.

Mengapa cangkuk, dan mengapa secara khusus useState dan useEffect?

Cangkuk telah ditambah dalam React 16.8 untuk membolehkan komponen fungsi menggunakan ciri keadaan dan kitaran hayat tanpa kelasSebelum itu, anda perlu menulis komponen kelas untuk mengekalkan keadaan setempat, melanggan data luaran atau bertindak balas terhadap peristiwa kitaran hayat seperti pemasangan dan penyahpasangan.

Masalah besar dengan kelas ialah logik yang berkaitan sering dibahagikan kepada pelbagai kaedah kitaran hayat seperti componentDidMount, componentDidUpdate and componentWillUnmountAnda akan mendapat cebisan ciri yang sama berselerak di sekitar kaedah yang berbeza berdasarkan apabila mereka berlari dan bukannya apa memang begitu, yang menjadikan kod lebih sukar untuk dibaca, diuji dan digunakan semula.

Cangkuk membalikkan model ini: dengan useState anda melampirkan keadaan terus kepada komponen fungsi, dan dengan useEffect Anda mengaitkan kesan sampingan terus kepada logik yang memerlukannya. Dengan cara itu, anda boleh mengumpulkan semua yang berkaitan dengan satu kebimbangan di satu tempat dan mengekstrak cangkuk yang boleh digunakan semula dengan mudah kemudian.

Antara semua cangkuk, useState and useEffect adalah primitif terasAnda boleh membina kebanyakan ciri harian hanya dengan dua ciri ini: keadaan UI seperti borang dan togol, permintaan rangkaian, langganan, pemasa, kemas kini DOM dan banyak lagi. Cangkuk lain (useRef, useReducer, useContext, useMemo...) memang hebat, tetapi ia dibina berdasarkan idea yang sama.

Peraturan cangkuk React yang anda tidak boleh putuskan

Cangkuk reaksi didatangkan dengan beberapa peraturan ketat yang menjadikannya berfungsi dengan andal merentasi renderJika anda melanggarnya, anda akan melihat sama ada ralat masa jalan atau pepijat yang sangat halus dan sukar untuk dinyahpepijat.

Peraturan pertama: panggil cangkuk hanya di dalam komponen fungsi React atau cangkuk tersuaiAnda tidak boleh menggunakan useState or useEffect dalam komponen kelas, fungsi utiliti biasa atau di luar mana-mana komponen. Corak seperti ini tidak sah:

import React, { Component, useState } from 'react';

class App extends Component {
  // ❌ This will throw - hooks don’t work in classes
  const  = useState(0);
  render() {
    return <h1>Hello, I am a Class Component!</h1>;
  }
}

Pendekatan yang betul adalah untuk beralih kepada komponen fungsi jika anda ingin menggunakan cangkuk:

import React, { useState } from 'react';

function App() {
  const  = useState('');

  return (
    <div>
      Your JSX code goes in here...
    </div>
  );
}

export default App;

Peraturan kedua: hanya panggil cangkuk di peringkat atas komponen andaIni bermakna tiada cangkuk di dalam gelung, syarat atau fungsi bersarang. React bergantung pada memanggil cangkuk dalam susunan yang sama pada setiap render untuk "memadankan" setiap satu. useState and useEffect panggilan dengan data yang disimpannya, jadi ini tidak sah:

function BadComponent({ enabled }) {
  if (enabled) {
    // ❌ Wrong: hook inside a conditional
    const  = useState(0);
  }
  // ...
}

Sebaliknya, isytiharkan cangkuk tanpa syarat di bahagian atas dan gunakan syarat di dalam kesan atau JSX. Cangkuk mesti sentiasa dipanggil, tetapi logik yang dijalankannya boleh bersyarat:

function ConditionalEffectComponent() {
  const  = useState(false);

  useEffect(() => {
    if (isMounted) {
      console.log('Component mounted');
    }
  }, );

  return (
    <div>
      <button onClick={() => setIsMounted(!isMounted)}>
        {isMounted ? 'Unmount' : 'Mount'}
      </button>
    </div>
  );
}

Peraturan tersirat ketiga ialah cangkuk mesti diimport daripada React (atau pustaka cangkuk), bukan dilaksanakan secara ad-hocIni jelas, tetapi perlu dinyatakan: keajaibannya terletak pada penghantar hook dalaman React yang menjejaki panggilan hook merentasi render.

Mengurus negeri tempatan dengan betul dengan useState

useState membolehkan anda melampirkan keadaan pada komponen fungsi dan menerima kedua-dua nilai semasa dan fungsi pengemas kiniSecara konseptual, ia merupakan rakan sejawat berfungsi bagi this.state and this.setState dalam komponen kelas.

Contoh kaunter minimum dengan useState kelihatan seperti ini:

import React, { useState } from 'react';

function Counter() {
  const  = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

export default Counter;

Semasa anda memanggil useState(initialValue), React menyimpan yang menyatakan dan mengembalikan pasangan: nilai keadaan semasa dan penetap. Tidak seperti pembolehubah setempat biasa, keadaan kekal merentasi pemaparan, jadi count nilai tidak ditetapkan semula kepada 0 setiap kali fungsi komponen berjalan.

Anda boleh menggunakan sebarang nilai boleh bersiri untuk keadaan: nombor, rentetan, boolean, tatasusunan, objek dan juga fungsi. Anda juga boleh menghubungi useState beberapa kali dalam komponen yang sama untuk memastikan nilai berkaitan berasingan dan bukannya memasukkan semuanya ke dalam satu objek.

Apabila nilai keadaan baharu bergantung pada yang sebelumnya, sentiasa gunakan borang kemas kini berfungsiIni mengelakkan pepijat apabila berbilang kemas kini keadaan berlaku secara berturut-turut:

setCount(prev => prev + 1);

Satu lagi butiran halus tetapi penting ialah memanggil setter menggantikan keseluruhan nilai keadaan, ia tidak menggabungkan objek seperti this.setState dalam kelasJika keadaan anda ialah objek atau tatasusunan, anda perlu menyebarkan nilai sebelumnya sendiri:

const  = useState({ name: 'Alex', age: 30 });

// ✅ Correct: copy and update
setUser(prev => ({ ...prev, age: prev.age + 1 }));

Untuk nilai awal yang mahal, anda boleh memulakan keadaan dengan perlahan dengan menghantar fungsi kepada useStateReact hanya akan memanggilnya pada render pertama:

const  = useState(() => calculateInitialValue());

Mengendalikan kesan sampingan dengan penggunaanKesan

useEffect ialah API React untuk menjalankan kesan sampingan dalam komponen fungsi"Kesan sampingan" ialah apa sahaja yang menyentuh dunia luar: pengambilan data, pembalakan, perubahan DOM langsung, langganan, pemasa, API pelayar, dsb.

Secara konsep, useEffect menggantikan gabungan componentDidMount, componentDidUpdate and componentWillUnmount daripada komponen kelasDaripada membahagikan satu kesan merentasi tiga kaedah kitaran hayat, anda mengisytiharkannya sekali dan biarkan React mengendalikan apabila ia berjalan dan apabila ia dibersihkan.

Tandatangan asas ialah useEffect(setup, dependencies?). Yang setup fungsi ialah badan kesan anda; ia secara pilihan boleh mengembalikan fungsi pembersihan. dependencies array memberitahu React bila kesan perlu dijalankan semula.

useEffect(() => {
  // side effect logic here

  return () => {
    // optional cleanup logic here
  };
}, );

Secara lalai, tanpa argumen kedua, kesannya akan berjalan selepas setiap pemaparan (pemasangan pertama dan setiap kemas kini berikutnya). Itu selalunya terlalu banyak untuk permintaan rangkaian atau logik yang mahal.

Corak yang sangat biasa adalah untuk mengemas kini sesuatu luaran apabila sesuatu keadaan berubahContohnya, mengemas kini tajuk halaman bergantung pada kiraan klik:

import React, { useState, useEffect } from 'react';

function Counter() {
  const  = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, ); // effect re-runs only when `count` changes

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increase</button>
    </div>
  );
}

Tatasusunan kebergantungan adalah penting untuk prestasi dan ketepatanIa mengawal bila React harus menjalankan semula kesan tersebut: jika sebarang kebergantungan telah berubah mengikut Object.is perbandingan, kesannya akan dibersihkan dan dijalankan semula; jika tiada perubahan, ia akan dilangkau.

Memahami tatasusunan kebergantungan seperti seorang profesional

Tatasusunan kebergantungan adalah tempat yang paling halus useEffect serangga datang dariReact membandingkan setiap elemen array dengan nilai sebelumnya menggunakan Object.isJika semua nilai adalah sama, kesannya akan dilangkau; jika sekurang-kurangnya satu berbeza, kesannya akan dilaksanakan semula.

Terdapat tiga konfigurasi kebergantungan utama yang akan anda gunakan sepanjang masa:

  • Tiada hujah kedua: kesannya berjalan selepas setiap pemaparan.
  • Tatasusunan kosong []: kesannya hanya berjalan sekali semasa pemasangan dan akan hilang semasa penyahpasangan.
  • Tatasusunan dengan nilai : kesannya berjalan selepas mount dan bila-bila masa sebarang kebergantungan berubah.

Apabila kebergantungan adalah nilai primitif (nombor, rentetan, boolean), ini adalah mudahMasalah bermula apabila anda meletakkan objek, tatasusunan atau fungsi di dalam kebergantungan, kerana kesamaan adalah berasaskan rujukan. Dua objek yang sama dengan rujukan yang berbeza dianggap "berbeza", yang menyebabkan ulangan pada setiap pemaparan.

Pertimbangkan kesan yang bergantung kepada team objek daripada alat peraga:

function Team({ team }) {
  useEffect(() => {
    console.log(team.id, team.active);
  }, ); // ⚠️ might re-run every render if `team` reference changes
}

Walaupun kandungan pasukan sebenar tidak berubah, rujukan objek baharu pada setiap pemaparan akan memaksa kesan dijalankan semulaUntuk mengelakkan perkara ini, sama ada bergantung pada medan primitif yang anda gunakan atau bina semula objek di dalam kesan itu sendiri.

Versi yang lebih selamat hanya menjejaki apa yang benar-benar diperlukan oleh kesannya:

function Team({ team }) {
  const { id, active } = team;

  useEffect(() => {
    console.log(id, active);
  }, );
}

Jika anda benar-benar memerlukan keseluruhan objek di dalam kesan tersebut, anda boleh menciptanya semula di sana dan bukannya menggunakannya sebagai kebergantunganDengan cara itu, senarai kebergantungan masih boleh berdasarkan primitif:

function Team({ team }) {
  const { id, active, name } = team;

  useEffect(() => {
    const localTeam = { id, active, name };
    // use `localTeam` here
  }, );
}

Sebagai pilihan terakhir, anda boleh menggunakan memoisasi dengan useMemo or useCallback untuk objek atau fungsi yang mahal, tetapi ingat bahawa memoisasi itu sendiri ada kosnya. Jangan taburkannya di mana-mana "sekiranya berlaku"; tambahkannya apabila kebergantungan tertentu benar-benar menyebabkan masalah prestasi.

Membersihkan kesan dengan betul

Sesetengah kesan sampingan memperuntukkan sumber yang mesti dilepaskan: langganan, soket, selang masa, tamat masa, pendengar acara, dsb. Terlupa untuk membersihkannya boleh menyebabkan kebocoran memori atau kerja yang digandakan dengan mudah.

In useEffect, pembersihan dikendalikan dengan mengembalikan fungsi daripada kesanReact akan memanggil fungsi ini sebelum menjalankan kesan sekali lagi dengan kebergantungan baharu, dan juga buat kali terakhir apabila komponen dinyahpasang.

import { useEffect } from 'react';

function LogMessage({ message }) {
  useEffect(() => {
    const log = setInterval(() => {
      console.log(message);
    }, 1000);

    return () => {
      clearInterval(log);
    };
  }, );

  return <div>logging to console "{message}"</div>;
}

Dalam contoh ini, setiap kali message perubahan, React akan mengosongkan selang masa lama terlebih dahulu, kemudian menyediakan selang masa baharu dengan mesej yang dikemas kiniApabila komponen hilang daripada UI, pembersihan terakhir akan mengosongkan selang masa tersebut buat selama-lamanya.

Gandingan "persediaan + pembersihan" ini adalah penting kepada model mental useEffectCuba fikirkan setiap kesan sebagai proses kendiri yang bermula dalam fungsi persediaan dan berhenti sepenuhnya dalam fungsi pembersihan. React mungkin menjalankan berbilang kitaran persediaan/pembersihan dalam pembangunan (terutamanya di bawah Mod Ketat) untuk menguji tekanan bahawa pembersihan anda benar-benar membatalkan segala-galanya.

Satu contoh klasik ialah melanggan sumber luaran, seperti API sembang atau peristiwa pelayar (lihat pengendalian onKeyDown dalam React):

useEffect(() => {
  function handleClick(event) {
    console.log('Clicked', event.clientX, event.clientY);
  }

  document.addEventListener('click', handleClick);

  return () => {
    document.removeEventListener('click', handleClick);
  };
}, []); // runs once on mount, cleans up on unmount

Menggunakan useState dan useEffect bersama untuk pengambilan data

Salah satu kombinasi dunia sebenar yang paling biasa ialah menggunakan useState and useEffect untuk mengambil data daripada APIAnda menyimpan data (dan mungkin bendera pemuatan/ralat) dalam keadaan dan melaksanakan permintaan dalam kesan yang berjalan apabila komponen dipasang atau apabila beberapa parameter berubah.

Corak asas untuk mengambil data sebaik sahaja dipasang kelihatan seperti ini:

import { useEffect, useState } from 'react';

function FetchItems() {
  const  = useState([]);

  useEffect(() => {
    let ignore = false;

    async function fetchItems() {
      try {
        const response = await fetch('/items');
        const fetchedItems = await response.json();
        if (!ignore) {
          setItems(fetchedItems);
        }
      } catch (error) {
        console.error('Error fetching items:', error);
      }
    }

    fetchItems();

    return () => {
      // avoid updating state if the component unmounted
      ignore = true;
    };
  }, []);

  return (
    <div>
      {items.map(item => (
        <div key={item.id ?? item}>{item.name ?? item}</div>
      ))}
    </div>
  );
}

Di sini, tatasusunan kebergantungan kosong memastikan permintaan berjalan tepat sekaliBahagian dalaman ignore flag ialah cara mudah untuk mengelakkan penetapan state pada komponen yang tidak dilekapkan sekiranya permintaan diselesaikan lewat.

Ia juga sangat biasa untuk menambah bendera pemuatan dan menunjukkan pemutar atau ruang letak semasa data sedang dalam perjalanan:

const Statistics = () => {
  const  = useState([]);
  const  = useState(true);

  useEffect(() => {
    const getStats = async () => {
      try {
        const statsData = await getData();
        setStats(statsData);
      } finally {
        setLoading(false);
      }
    };

    getStats();
  }, []);

  if (loading) {
    return <div>Loading statistics...</div>;
  }

  return (
    <ul>
      {stats.map(stat => (
        <li key={stat.id}>{stat.label}: {stat.value}</li>
      ))}
    </ul>
  );
};

Jika pertanyaan anda bergantung pada parameter (seperti kategori, penapis atau parameter laluan), tambahkan parameter tersebut pada tatasusunan kebergantungan jadi kesannya berulang apabila ia berubah:

useEffect(() => {
  async function fetchItems() {
    const response = await fetch(`/items?category=${category}`);
    const data = await response.json();
    setItems(data);
  }

  fetchItems();
}, );

Berfikir dalam "kesan pada setiap pemaparan" vs "kitaran hayat"

Jika anda sudah biasa dengan komponen kelas, ia mungkin menggoda untuk memetakan secara mental useEffect untuk memasang/mengemas kini/menyahpasang kaedah, tetapi itu biasanya membawa kepada lebih banyak kekeliruan. Model mental yang lebih mudah ialah: "kesan dijalankan selepas pemaparan, dan mungkin akan hilang sebelum pemaparan seterusnya".

Dalam kelas, anda sering terpaksa menggandakan logik antara componentDidMount and componentDidUpdate kerana anda mahukan kesan yang sama dijalankan pada pemasangan dan kemas kini. Dengan hook, duplikasi itu hilang: satu kesan merangkumi kedua-dua kes dan React akan menguruskan pembersihan antara larian.

Reka bentuk ini juga menghapuskan keseluruhan kelas pepijat berkaitan dengan tidak mengendalikan kemas kini dengan betulContohnya, dalam komponen kelas yang melanggan status dalam talian rakan, mudah untuk terlupa untuk melanggan semula apabila props.friend perubahan, menyebabkan langganan basi atau ranap semasa dinyahpasang. Dengan useEffect yang menyenaraikan friend.id sebagai kebergantungan, React akan menjalankan pembersihan secara automatik untuk rakan lama dan persediaan untuk rakan baharu.

Perlu diingat bahawa dalam pembangunan Mod Ketat, React sengaja menjalankan kitaran persediaan + pembersihan anda dua kali semasa pemasangan.Ini tidak berlaku dalam pengeluaran, tetapi ia merupakan ujian tekanan yang berguna untuk mengesahkan bahawa pembersihan anda benar-benar membatalkan segala-galanya dan kesan anda boleh berjalan dengan selamat berbilang kali.

Mengoptimumkan dan menyelesaikan masalah penggunaan Tingkah laku Kesan

Apabila kesan berjalan lebih kerap daripada yang anda jangkakan, perkara pertama yang perlu diperiksa ialah tatasusunan kebergantunganSama ada kebergantungan berubah pada setiap render (biasa dengan objek/fungsi sebaris) atau anda terlupa untuk menentukan array sama sekali.

Merekod nilai kebergantungan adalah cara cepat untuk menyahpepijat:

useEffect(() => {
  console.log('Effect deps:', dep1, dep2);
}, );

Jika anda melihat log yang berbeza setiap kali, periksa kebergantungan yang sebenarnya berubah. Selalunya anda akan menemui objek sebaris atau fungsi anak panah yang dicipta semula setiap pemaparan. Memindahkan penciptaan objek di dalam kesan, atau mengangkat fungsi di luar komponen, atau memokannya dengan useCallback boleh menstabilkan kebergantungan apabila diperlukan.

Gelung tak terhingga berlaku apabila kesan bergantung pada nilai dan mengemas kini nilai yang sama tanpa syarat. Sebagai contoh:

useEffect(() => {
  setCount(count + 1); // ⚠️ will cause a loop if `count` is a dependency
}, );

Setiap masa count perubahan, kesan berjalan, kemas kini count sekali lagi, mencetuskan pemaparan lain, dan sebagainyaUntuk memecahkan corak tersebut, pertimbangkan sama ada kemas kini keadaan benar-benar tergolong dalam sesuatu kesan, sama ada ia harus dicetuskan oleh interaksi pengguna atau sama ada anda boleh bergantung pada nilai yang berbeza.

Kadangkala anda ingin membaca nilai terkini bagi sesetengah keadaan atau prop di dalam kesan tanpa nilai tersebut mencetuskan ulangan.Dalam senario lanjutan tersebut, API yang lebih baharu seperti "peristiwa kesan" (melalui useEffectEvent dalam dokumen React) atau rujukan boleh membantu, tetapi bagi kebanyakan kes praktikal, kekal setia kepada kebergantungan adalah lebih selamat dan mudah.

Menyatukan semuanya, menggunakan useState and useEffect dengan betul merujuk kepada beberapa tabiat teras: pastikan keadaan kecil dan fokus, lebih suka kemas kini berfungsi apabila memperoleh keadaan baharu daripada keadaan lama, strukturkan kesan di sekitar pasangan persediaan/pembersihan, bersikap jujur ​​dan eksplisit dengan tatasusunan kebergantungan dan sentiasa hormati peraturan cangkuk supaya React boleh menjejaki dengan andal apa yang sepatutnya berada di mana. Apabila anda mengikuti prinsip tersebut, komponen anda kekal boleh diramal, kesan sampingan anda berkelakuan dan pangkalan kod React anda menjadi lebih mudah untuk berkembang apabila aplikasi anda berkembang.

artikel berkaitan:
Selesai: Cara memasang cangkuk asli bertindak balas dengan
Related posts: