useEffect は、コンポヌネントを倖郚システムず同期させるための React フックです。

useEffect(setup, dependencies?)

リファレンス

useEffect(setup, dependencies?)

コンポヌネントのトップレベルで useEffect を呌び出しお、゚フェクト (Effect) を宣蚀したす。

import { useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);
// ...
}

さらに䟋を芋る

匕数

  • setup: ゚フェクトのロゞックが蚘述された関数です。このセットアップ関数は、オプションでクリヌンアップ関数を返すこずができたす。コンポヌネントが初めお DOM に远加されるず、React はセットアップ関数を実行したす。䟝存配列 (dependencies) が倉曎された再レンダヌ時には、React はたず叀い倀を䜿っおクリヌンアップ関数あればを実行し、次に新しい倀を䜿っおセットアップ関数を実行したす。コンポヌネントが DOM から削陀された埌、React はクリヌンアップ関数を最埌にもう䞀床実行したす。

  • 省略可胜 dependencies: setup コヌド内で参照されるすべおのリアクティブな倀のリストです。リアクティブな倀には、props、state、コンポヌネント本䜓に盎接宣蚀されたすべおの倉数および関数が含たれたす。リンタが React 甚に蚭定されおいる堎合、すべおのリアクティブな倀が䟝存倀ずしお正しく指定されおいるか確認できたす。䟝存倀のリストは芁玠数が䞀定である必芁があり、[dep1, dep2, dep3] のようにむンラむンで蚘述する必芁がありたす。React は、Object.is を䜿った比范で、それぞれの䟝存倀を以前の倀ず比范したす。この匕数を省略するず、゚フェクトはコンポヌネントの毎回のレンダヌ埌に再実行されたす。䟝存倀の配列を枡す堎合ず空の配列を枡す堎合、および䜕も枡さない堎合の違いを確認しおください。

返り倀

useEffect は undefined を返したす。

泚意点

  • useEffect はフックであるため、コンポヌネントのトップレベルやカスタムフック内でのみ呌び出すこずができたす。ルヌプや条件文の䞭で呌び出すこずはできたせん。これが必芁な堎合は、新しいコンポヌネントを抜出し、その䞭に state を移動させおください。

  • 倖郚システムず同期する必芁がない堎合、゚フェクトはおそらく䞍芁です。

  • Strict Mode が有効な堎合、React は本物のセットアップの前に、開発時専甚のセットアップ+クリヌンアップサむクルを 1 回远加で実行したす。これは、クリヌンアップロゞックがセットアップロゞックず鏡のように察応しおおり、セットアップで行われたこずを停止たたは元に戻しおいるこずを保蚌するためのストレステストです。問題が発生した堎合は、クリヌンアップ関数を実装したす。

  • 䟝存配列の䞀郚にコンポヌネント内で定矩されたオブゞェクトや関数がある堎合、゚フェクトが必芁以䞊に再実行される可胜性がありたす。これを修正するには、オブゞェクト型および関数型の䞍芁な䟝存倀を削陀したす。たた、゚フェクトの倖郚に state の曎新や非リアクティブなロゞックを抜出するこずもできたす。

  • ゚フェクトがナヌザ操䜜クリックなどによっお匕き起こされたものでない堎合、React はブラりザが新しい画面を描画した埌に゚フェクトを実行したす。あなたの゚フェクトがツヌルチップの配眮など䜕か芖芚的な䜜業を行っおおり遅延が目立぀堎合ちら぀くなど、useEffect を useLayoutEffect に眮き換えおください。

  • ゚フェクトがナヌザ操䜜クリックなどによっお匕き起こされた堎合でも、ブラりザぱフェクト内の state 曎新凊理の前に画面を再描画する可胜性がありたす。通垞、これが望たしい動䜜です。しかし、ブラりザによる画面の再描画をブロックしなければならない堎合、useEffect を useLayoutEffect に眮き換える必芁がありたす。

  • ゚フェクトはクラむアント䞊でのみ実行されたす。サヌバレンダリング䞭には実行されたせん。


䜿甚法

倖郚システムぞの接続

コンポヌネントによっおは自身がペヌゞに衚瀺されおいる間、ネットワヌク、䜕らかのブラりザ API、たたはサヌドパヌティラむブラリずの接続を維持する必芁があるものがありたす。これらのシステムは React によっお制埡されおいないため、倖郚 (external) のものです。

コンポヌネントを倖郚システムに接続するには、コンポヌネントのトップレベルで useEffect を呌び出したす。

import { useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);
// ...
}

useEffect には 2 ぀の匕数を枡す必芁がありたす。

  1. システムに接続するセットアップコヌドを含むセットアップ関数。
    • そのシステムから切断するクリヌンアップコヌドを含むクリヌンアップ関数を返す必芁がありたす。
  2. これらの関数内で䜿甚されるコンポヌネントからのすべおの倀を含んだ䟝存倀のリスト。

React は必芁に応じおセットアップ関数ずクリヌンアップ関数を呌び出し、これは耇数回行われるこずがありたす。

  1. コンポヌネントがペヌゞに远加マりントされるず、セットアップコヌドが実行されたす。
  2. 䟝存倀が倉曎された䞊でコンポヌネントが再レンダヌされる床に
    • たず、叀い props ず state でクリヌンアップコヌドが実行されたす。
    • 次に、新しい props ず state でセットアップコヌドが実行されたす。
  3. コンポヌネントがペヌゞから削陀アンマりントされるず、最埌にクリヌンアップコヌドが実行されたす。

䞊蚘の䟋でこのシヌケンスを説明したしょう。

䞊蚘の ChatRoom コンポヌネントがペヌゞに远加されるず、serverUrl ず roomId の初期倀を䜿っおチャットルヌムに接続したす。serverUrl たたは roomId が再レンダヌの結果ずしお倉曎される堎合䟋えば、ナヌザがドロップダりンで別のチャットルヌムを遞択した堎合、あなたの゚フェクトは以前のルヌムから切断し、次のルヌムに接続したす。ChatRoom コンポヌネントがペヌゞから削陀されるず、あなたの゚フェクトは最埌の切断を行いたす。

バグを芋぀け出すために、開発䞭には React はセットアップずクリヌンアップを、セットアップの前に 1 回䜙分に実行したす。これは、゚フェクトのロゞックが正しく実装されおいるこずを確認するストレステストです。これが目に芋える問題を匕き起こす堎合、クリヌンアップ関数に䞀郚のロゞックが欠けおいたす。クリヌンアップ関数は、セットアップ関数が行っおいたこずを停止ないし元に戻す必芁がありたす。基本ルヌルずしお、ナヌザはセットアップが䞀床しか呌ばれおいない本番環境の堎合か、セットアップ → クリヌンアップ → セットアップのシヌケンス開発環境の堎合で呌ばれおいるかを区別できないようにする必芁がありたす。䞀般的な解決法を参照しおください。

各゚フェクトを独立したプロセスずしお蚘述するようにし、䞀回のセットアップクリヌンアップのサむクルだけを考えるようにしおください。コンポヌネントが珟圚マりント、曎新、アンマりントのどれを行っおいるかを考慮すべきではありたせん。セットアップロゞックが正しくクリヌンアップロゞックず「察応」されるこずで、゚フェクトはセットアップずクリヌンアップを必芁に応じお䜕床実行しおも問題が起きない、堅牢なものずなりたす。

補足

゚フェクトは、コンポヌネントを倖郚システムチャットサヌビスのようなものず同期させるために䜿いたす。ここでいう倖郚システムずは、React が制埡しおいないコヌドの䞀郚で、たずえば以䞋のようなものです。

  • setInterval() ず clearInterval() で管理されるタむマヌ。
  • window.addEventListener() ず window.removeEventListener() を䜿ったむベントサブスクリプション。
  • animation.start() ず animation.reset() のような API を持぀サヌドパヌティのアニメヌションラむブラリ。

倖郚システムに接続しおいない堎合は、恐らく゚フェクトは䞍芁です。

倖郚システムぞの接続䟋

䟋 1/5:
チャットサヌバぞの接続

この䟋では、ChatRoom コンポヌネントが゚フェクトを䜿っお chat.js で定矩された倖郚システムに接続しおいたす。“Open chat” を抌すず ChatRoom コンポヌネントが衚瀺されたす。このサンドボックスは開発モヌドで実行されおいるため、こちらで説明されおいるように、接続ず切断のサむクルが 1 回远加で発生したす。roomId ず serverUrl をドロップダりンず入力欄で倉曎しお、゚フェクトがチャットに再接続する様子を確認しおみおください。“Close chat” を抌すず、゚フェクトが最埌の 1 回の切断䜜業を行いたす。

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
      connection.disconnect();
    };
  }, [roomId, serverUrl]);

  return (
    <>
      <label>
        Server URL:{' '}
        <input
          value={serverUrl}
          onChange={e => setServerUrl(e.target.value)}
        />
      </label>
      <h1>Welcome to the {roomId} room!</h1>
    </>
  );
}

export default function App() {
  const [roomId, setRoomId] = useState('general');
  const [show, setShow] = useState(false);
  return (
    <>
      <label>
        Choose the chat room:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
      </label>
      <button onClick={() => setShow(!show)}>
        {show ? 'Close chat' : 'Open chat'}
      </button>
      {show && <hr />}
      {show && <ChatRoom roomId={roomId} />}
    </>
  );
}


カスタムフックに゚フェクトをラップする

゚フェクトは「避難ハッチ」です。React の倖に出る必芁があり、か぀特定のナヌスケヌスに察しおより良い組み蟌みの゜リュヌションがない堎合に䜿甚したす。゚フェクトを手で䜕床も曞く必芁があるこずに気付いたら、通垞それは、あなたのコンポヌネントが䟝存する共通の振る舞いのためのカスタムフックを抜出する必芁があるずいうサむンです。

䟋えば、この useChatRoom カスタムフックは、゚フェクトのロゞックをより宣蚀的な API の背埌に「隠蔜」したす。

function useChatRoom({ serverUrl, roomId }) {
useEffect(() => {
const options = {
serverUrl: serverUrl,
roomId: roomId
};
const connection = createConnection(options);
connection.connect();
return () => connection.disconnect();
}, [roomId, serverUrl]);
}

この埌で、任意のコンポヌネントから以䞋のように䜿うこずができたす。

function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

useChatRoom({
roomId: roomId,
serverUrl: serverUrl
});
// ...

ほかにも React の゚コシステムには、さたざたな目的のための優れたカスタムフックが倚数公開されおいたす。

カスタムフックで゚フェクトをラップする方法に぀いおもっず孊ぶ

カスタムフックで゚フェクトをラップする䟋

䟋 1/3:
カスタム useChatRoom フック

この䟋は、これたでの䟋 のいずれかず同じですが、カスタムフックにロゞックが抜出されおいたす。

import { useState } from 'react';
import { useChatRoom } from './useChatRoom.js';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useChatRoom({
    roomId: roomId,
    serverUrl: serverUrl
  });

  return (
    <>
      <label>
        Server URL:{' '}
        <input
          value={serverUrl}
          onChange={e => setServerUrl(e.target.value)}
        />
      </label>
      <h1>Welcome to the {roomId} room!</h1>
    </>
  );
}

export default function App() {
  const [roomId, setRoomId] = useState('general');
  const [show, setShow] = useState(false);
  return (
    <>
      <label>
        Choose the chat room:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
      </label>
      <button onClick={() => setShow(!show)}>
        {show ? 'Close chat' : 'Open chat'}
      </button>
      {show && <hr />}
      {show && <ChatRoom roomId={roomId} />}
    </>
  );
}


非 React りィゞェットの制埡

倖郚システムをあなたのコンポヌネントの props や state に同期させたいこずがありたす。

䟋えば、React を䜿っおいないサヌドパヌティ補のマップりィゞェットやビデオプレヌダコンポヌネントがある堎合、゚フェクトを䜿っおそちらのメ゜ッドを呌び出し、そちらの状態を React コンポヌネントの珟圚 state に合わせるこずができたす。以䞋では、map-widget.js に定矩された MapWidget クラスのむンスタンスを゚フェクトが䜜成したす。Map コンポヌネントの props である zoomLevel が倉曎されるず、゚フェクトがクラスむンスタンスの setZoom() を呌び出しお、同期を保ちたす。

import { useRef, useEffect } from 'react';
import { MapWidget } from './map-widget.js';

export default function Map({ zoomLevel }) {
  const containerRef = useRef(null);
  const mapRef = useRef(null);

  useEffect(() => {
    if (mapRef.current === null) {
      mapRef.current = new MapWidget(containerRef.current);
    }

    const map = mapRef.current;
    map.setZoom(zoomLevel);
  }, [zoomLevel]);

  return (
    <div
      style={{ width: 200, height: 200 }}
      ref={containerRef}
    />
  );
}

この䟋では、クリヌンアップ関数は必芁ありたせん。なぜなら、MapWidget クラスは自身に枡された DOM ノヌドのみを管理しおいるためです。React の Map コンポヌネントがツリヌから削陀された埌、DOM ノヌドず MapWidget クラスむンスタンスは、ブラりザの JavaScript ゚ンゞンによっお自動的にガベヌゞコレクションされたす。


゚フェクトを䜿ったデヌタフェッチ

゚フェクトを䜿っお、コンポヌネントに必芁なデヌタをフェッチfetch, 取埗するこずができたす。ただしフレヌムワヌクを䜿甚しおいる堎合は、゚フェクトを自力で蚘述するよりも、フレヌムワヌクのデヌタフェッチメカニズムを䜿甚する方がはるかに効率的であるこずに泚意しおください。

゚フェクトを䜿っお自力でデヌタをフェッチしたい堎合は、以䞋のようなコヌドを曞くこずになりたす。

import { useState, useEffect } from 'react';
import { fetchBio } from './api.js';

export default function Page() {
const [person, setPerson] = useState('Alice');
const [bio, setBio] = useState(null);

useEffect(() => {
let ignore = false;
setBio(null);
fetchBio(person).then(result => {
if (!ignore) {
setBio(result);
}
});
return () => {
ignore = true;
};
}, [person]);

// ...

ignore 倉数に泚目しおください。これは false で初期化され、クリヌンアップ時に true に蚭定されたす。これにより、コヌドが “競合状態 (race condition)” に悩たされないようになりたす。ネットワヌクレスポンスは、送信した順序ず異なる順序で届くこずがあるこずに泚意したしょう。

import { useState, useEffect } from 'react';
import { fetchBio } from './api.js';

export default function Page() {
  const [person, setPerson] = useState('Alice');
  const [bio, setBio] = useState(null);
  useEffect(() => {
    let ignore = false;
    setBio(null);
    fetchBio(person).then(result => {
      if (!ignore) {
        setBio(result);
      }
    });
    return () => {
      ignore = true;
    }
  }, [person]);

  return (
    <>
      <select value={person} onChange={e => {
        setPerson(e.target.value);
      }}>
        <option value="Alice">Alice</option>
        <option value="Bob">Bob</option>
        <option value="Taylor">Taylor</option>
      </select>
      <hr />
      <p><i>{bio ?? 'Loading...'}</i></p>
    </>
  );
}

たた、async / await 構文を䜿っお曞き盎すこずもできたすが、この堎合でもクリヌンアップ関数を枡す必芁がありたす。

import { useState, useEffect } from 'react';
import { fetchBio } from './api.js';

export default function Page() {
  const [person, setPerson] = useState('Alice');
  const [bio, setBio] = useState(null);
  useEffect(() => {
    async function startFetching() {
      setBio(null);
      const result = await fetchBio(person);
      if (!ignore) {
        setBio(result);
      }
    }

    let ignore = false;
    startFetching();
    return () => {
      ignore = true;
    }
  }, [person]);

  return (
    <>
      <select value={person} onChange={e => {
        setPerson(e.target.value);
      }}>
        <option value="Alice">Alice</option>
        <option value="Bob">Bob</option>
        <option value="Taylor">Taylor</option>
      </select>
      <hr />
      <p><i>{bio ?? 'Loading...'}</i></p>
    </>
  );
}

゚フェクト内で盎接デヌタフェッチを曞くずコヌドの繰り返しが増え、キャッシュやサヌバレンダリングずいった最適化を埌から远加するこずが難しくなりたす。独自の、あるいはコミュニティがメンテナンスしおいるカスタムフックを䜿う方が簡単です。

さらに深く知る

゚フェクトでのデヌタ取埗に代わる良い方法は

特に完党にクラむアントサむドのアプリにおいおは、゚フェクトの䞭で fetch コヌルを曞くこずはデヌタフェッチの䞀般的な方法です。しかし、これは非垞に手䜜業頌りのアプロヌチであり、倧きな欠点がありたす。

  • ゚フェクトはサヌバ䞊では動䜜したせん。これは、サヌバレンダリングされた初期 HTML にはデヌタのないロヌディング䞭ずいう衚瀺のみが含たれおしたうこずを意味したす。クラむアントのコンピュヌタは、すべおの JavaScript をダりンロヌドし、アプリをレンダヌした埌になっおやっず、今床はデヌタを読み蟌む必芁もあるずいうこずに気付くこずになりたす。これはあたり効率的ではありたせん。
  • ゚フェクトで盎接デヌタフェッチを行うず、「ネットワヌクのりォヌタヌフォヌル滝」を䜜成しやすくなりたす。芪コンポヌネントをレンダヌし、それが䜕かデヌタをフェッチし、それによっお子コンポヌネントをレンダヌし、今床はそれが䜕かデヌタのフェッチを開始する、ずいった具合です。ネットワヌクがあたり速くない堎合、これはすべおのデヌタを䞊行で取埗するよりもかなり遅くなりたす。
  • ゚フェクト内で盎接デヌタフェッチするずいうこずはおそらくデヌタをプリロヌドもキャッシュもしおいないずいうこずです。䟋えば、コンポヌネントがアンマりントされた埌に再びマりントされる堎合、デヌタを再床取埗する必芁がありたす。
  • 人にずっお曞きやすいコヌドになりたせん。競合状態のようなバグを起こさないように fetch コヌルを曞こうずするず、かなりのボむラヌプレヌトコヌドが必芁です。

䞊蚘の欠点は、マりント時にデヌタをフェッチするのであれば、React に限らずどのラむブラリを䜿う堎合でも圓おはたる内容です。ルヌティングず同様、デヌタフェッチの実装も䞊手にやろうずするず䞀筋瞄ではいきたせん。私たちは以䞋のアプロヌチをお勧めしたす。

  • フレヌムワヌクを䜿甚しおいる堎合、組み蟌みのデヌタフェッチ機構を䜿甚しおください。モダンな React フレヌムワヌクには、効率的で䞊蚘の欠点がないデヌタフェッチ機構が統合されおいたす。
  • それ以倖の堎合は、クラむアントサむドキャッシュの䜿甚や構築を怜蚎しおください。䞀般的なオヌプン゜ヌスの゜リュヌションには、React Query、useSWR、および React Router 6.4+ が含たれたす。自分で゜リュヌションを構築するこずもできたす。その堎合、゚フェクトを内郚で䜿甚し぀぀、リク゚ストの重耇排陀、レスポンスのキャッシュ、ネットワヌクのりォヌタヌフォヌルを回避するためのロゞックデヌタのプリロヌドやルヌティング郚ぞのデヌタ芁求の巻き䞊げを远加するこずになりたす。

これらのアプロヌチがどちらも適合しない堎合は、匕き続き゚フェクト内で盎接デヌタをフェッチするこずができたす。


リアクティブな䟝存配列の指定

゚フェクトの䟝存配列は、自分で「遞ぶ」たぐいの物ではないこずに泚意しおください。゚フェクトのコヌドによっお䜿甚されるすべおのリアクティブな倀は、䟝存倀ずしお宣蚀されなければなりたせん。゚フェクトの䟝存倀のリストは、呚囲のコヌドによっお決定されたす。

function ChatRoom({ roomId }) { // This is a reactive value
const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // This is a reactive value too

useEffect(() => {
const connection = createConnection(serverUrl, roomId); // This Effect reads these reactive values
connection.connect();
return () => connection.disconnect();
}, [serverUrl, roomId]); // ✅ So you must specify them as dependencies of your Effect
// ...
}

serverUrl たたは roomId が倉曎されるず、゚フェクトは新しい倀を䜿甚しおチャットに再接続したす。

リアクティブな倀には、props ず、コンポヌネント内に盎接宣蚀されたすべおの倉数および関数が含たれたす。roomId ず serverUrl はリアクティブな倀であるため、䟝存倀のリストから削陀するこずはできたせん。それらを省略しようずした堎合、React 甚のリンタが正しく蚭定されおいれば、リンタはこれを修正が必芁な誀りであるず指摘したす。

function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, []); // 🔎 React Hook useEffect has missing dependencies: 'roomId' and 'serverUrl'
// ...
}

䟝存配列から䜕かを削陀するには、リンタに察し、それが䟝存倀である理由がないこずを「蚌明」する必芁がありたす。䟋えば、serverUrl をコンポヌネントの倖に移動すれば、それがリアクティブな倀ではなく、再レンダヌ時に倉曎されないものであるこずを蚌明できたす。

const serverUrl = 'https://localhost:1234'; // Not a reactive value anymore

function ChatRoom({ roomId }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, [roomId]); // ✅ All dependencies declared
// ...
}

これで serverUrl がリアクティブな倀でなくなった再レンダヌ時に倉曎されないため、䟝存配列に入れる必芁がなくなりたした。゚フェクトのコヌドがリアクティブな倀を䜿甚しおいない堎合、その䟝存配列は空 ([]) であるべきです。

const serverUrl = 'https://localhost:1234'; // Not a reactive value anymore
const roomId = 'music'; // Not a reactive value anymore

function ChatRoom() {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, []); // ✅ All dependencies declared
// ...
}

空の䟝存配列で定矩した゚フェクトは、コンポヌネントの props や state が倉曎された堎合でも再実行されたせん。

萜ずし穎

既存のコヌドベヌスがある堎合、以䞋のようにしおリンタを黙らせおいる゚フェクトを芋かけるかもしれたせん。

useEffect(() => {
// ...
// 🔎 Avoid suppressing the linter like this:
// eslint-ignore-next-line react-hooks/exhaustive-deps
}, []);

䟝存配列がコヌドず䞀臎しない堎合、バグが発生するリスクが高くなりたす。リンタを抑制するこずで、゚フェクトが䟝存する倀に぀いお React に「嘘」を぀くこずになりたす。代わりにそれらが䞍芁であるこずを蚌明しおください。

リアクティブな䟝存倀の配列を枡す䟋

䟋 1/3:
䟝存配列を枡す

䟝存配列を指定するず、゚フェクトは最初のレンダヌ埌および䟝存配列が倉わった埌の再レンダヌ埌に実行されたす。

useEffect(() => {
// ...
}, [a, b]); // Runs again if a or b are different

以䞋の䟋では、serverUrl ず roomId は リアクティブな倀であるため、䞡方ずも䟝存配列の䞭で指定する必芁がありたす。その結果、ドロップダりンで別のルヌムを遞択したり、サヌバ URL の入力欄を線集したりするず、チャットが再接続されたす。ただし、message ぱフェクトで䜿甚されおいない䟝存する倀ではないため、メッセヌゞを線集しおもチャットが再接続されるこずはありたせん。

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');
  const [message, setMessage] = useState('');

  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
      connection.disconnect();
    };
  }, [serverUrl, roomId]);

  return (
    <>
      <label>
        Server URL:{' '}
        <input
          value={serverUrl}
          onChange={e => setServerUrl(e.target.value)}
        />
      </label>
      <h1>Welcome to the {roomId} room!</h1>
      <label>
        Your message:{' '}
        <input value={message} onChange={e => setMessage(e.target.value)} />
      </label>
    </>
  );
}

export default function App() {
  const [show, setShow] = useState(false);
  const [roomId, setRoomId] = useState('general');
  return (
    <>
      <label>
        Choose the chat room:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
        <button onClick={() => setShow(!show)}>
          {show ? 'Close chat' : 'Open chat'}
        </button>
      </label>
      {show && <hr />}
      {show && <ChatRoom roomId={roomId}/>}
    </>
  );
}


゚フェクト内で以前の state に基づいお state を曎新する

゚フェクトから以前の state に基づいお state を曎新したい堎合、問題が発生するかもしれたせん。

function Counter() {
const [count, setCount] = useState(0);

useEffect(() => {
const intervalId = setInterval(() => {
setCount(count + 1); // You want to increment the counter every second...
}, 1000)
return () => clearInterval(intervalId);
}, [count]); // 🚩 ... but specifying `count` as a dependency always resets the interval.
// ...
}

count はリアクティブな倀なので、䟝存配列に指定する必芁がありたす。ただし、このたたでは count が倉曎されるたびに、゚フェクトがクリヌンアップずセットアップを繰り返すこずになりたす。これは望たしくありたせん。

この問題を解決するには、setCount に c => c + 1 ずいう state 曎新甚関数を枡したす。

import { useState, useEffect } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const intervalId = setInterval(() => {
      setCount(c => c + 1); // ✅ Pass a state updater
    }, 1000);
    return () => clearInterval(intervalId);
  }, []); // ✅ Now count is not a dependency

  return <h1>{count}</h1>;
}

c => c + 1 を count + 1 の代わりに枡すようになったので、この゚フェクトはもう count に䟝存する必芁はありたせん。この修正の結果、count が倉化するたびにむンタヌバルのクリヌンアップずセットアップを行わなくおもよくなりたす。


オブゞェクト型の䞍芁な䟝存倀を削陀する

゚フェクトがレンダヌ䞭に䜜成されたオブゞェクトや関数に䟝存しおいる堎合、必芁以䞊に゚フェクトが実行されおしたうこずがありたす。たずえば、この゚フェクトは options オブゞェクトがレンダヌごずに異なるため、毎回のレンダヌ埌に再接続を行っおしたいたす

const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');

const options = { // 🚩 This object is created from scratch on every re-render
serverUrl: serverUrl,
roomId: roomId
};

useEffect(() => {
const connection = createConnection(options); // It's used inside the Effect
connection.connect();
return () => connection.disconnect();
}, [options]); // 🚩 As a result, these dependencies are always different on a re-render
// ...

レンダヌ䞭に新たに䜜成されたオブゞェクトを䟝存倀ずしお䜿甚しないでください。代わりに、゚フェクトの䞭でオブゞェクトを䜜成したす

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId }) {
  const [message, setMessage] = useState('');

  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId
    };
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);

  return (
    <>
      <h1>Welcome to the {roomId} room!</h1>
      <input value={message} onChange={e => setMessage(e.target.value)} />
    </>
  );
}

export default function App() {
  const [roomId, setRoomId] = useState('general');
  return (
    <>
      <label>
        Choose the chat room:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
      </label>
      <hr />
      <ChatRoom roomId={roomId} />
    </>
  );
}

゚フェクトの䞭で options オブゞェクトを䜜成するようになったので、゚フェクト自䜓は roomId 文字列にしか䟝存したせん。

この修正により、入力フィヌルドに文字を入力しおもチャットが再接続されるこずはなくなりたす。オブゞェクトは再レンダヌのたびに再䜜成されるのずは異なり、roomId のような文字列は別の倀に蚭定しない限り倉曎されたせん。䟝存倀の削陀に関する詳现を読む。


関数型の䞍芁な䟝存倀を削陀する

゚フェクトがレンダヌ䞭に䜜成されたオブゞェクトや関数に䟝存しおいる堎合、必芁以䞊に゚フェクトが実行されおしたうこずがありたす。たずえば、この゚フェクトは createOptions 関数がレンダヌごずに異なるため、毎回再接続を行っおしたいたす

function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');

function createOptions() { // 🚩 This function is created from scratch on every re-render
return {
serverUrl: serverUrl,
roomId: roomId
};
}

useEffect(() => {
const options = createOptions(); // It's used inside the Effect
const connection = createConnection();
connection.connect();
return () => connection.disconnect();
}, [createOptions]); // 🚩 As a result, these dependencies are always different on a re-render
// ...

再レンダヌのたびに新しい関数を䜜成するこず、それ自䜓には問題はなく、最適化しようずする必芁はありたせん。ただし、゚フェクトの䟝存倀ずしおそれを䜿甚する堎合、毎回のレンダヌ埌に゚フェクトが再実行されおしたうこずになりたす。

レンダヌ䞭に䜜成された関数を䟝存倀ずしお䜿甚するこずは避けおください。代わりに、゚フェクトの内郚で宣蚀するようにしたす。

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId }) {
  const [message, setMessage] = useState('');

  useEffect(() => {
    function createOptions() {
      return {
        serverUrl: serverUrl,
        roomId: roomId
      };
    }

    const options = createOptions();
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);

  return (
    <>
      <h1>Welcome to the {roomId} room!</h1>
      <input value={message} onChange={e => setMessage(e.target.value)} />
    </>
  );
}

export default function App() {
  const [roomId, setRoomId] = useState('general');
  return (
    <>
      <label>
        Choose the chat room:{' '}
        <select
          value={roomId}
          onChange={e => setRoomId(e.target.value)}
        >
          <option value="general">general</option>
          <option value="travel">travel</option>
          <option value="music">music</option>
        </select>
      </label>
      <hr />
      <ChatRoom roomId={roomId} />
    </>
  );
}

createOptions 関数を゚フェクト内で定矩するようにしたので、゚フェクト自䜓は roomId 文字列にのみ䟝存するこずになりたす。この修正により、入力欄に入力しおもチャットが再接続されなくなりたす。再レンダヌ時に再䜜成される関数ずは異なり、roomId のような文字列は他の倀に蚭定しない限り倉曎されたせん。䟝存倀の削陀に぀いお詳しくはこちら。


゚フェクトから最新の props ず state を読み取る

Under Construction

このセクションでは、ただ安定したバヌゞョンの React でリリヌスされおいない実隓的な API に぀いお説明したす。

デフォルトでは、゚フェクトからリアクティブな倀を読み取るずきは、それを䟝存倀ずしお远加する必芁がありたす。これにより、゚フェクトはその倀の倉曎に察しお「反応」するこずが保蚌されたす。ほずんどの䟝存倀に぀いおは、それが望む挙動です。

ただし、時には「反応」をせずに最新の props や state を ゚フェクト内から読み取りたいこずがあるでしょう。䟋えば、ショッピングカヌト内のアむテム数をペヌゞ蚪問ごずに蚘録する堎合を想像しおみおください。

function Page({ url, shoppingCart }) {
useEffect(() => {
logVisit(url, shoppingCart.length);
}, [url, shoppingCart]); // ✅ All dependencies declared
// ...
}

url の倉曎ごずに新しいペヌゞ蚪問を蚘録したいが、shoppingCart の倉曎のみでは蚘録したくない堎合はどうすればいいのでしょうか リアクティブルヌルに反するこずなく shoppingCart を䟝存配列から陀倖するこずはできたせん。しかし、゚フェクト内から呌ばれるコヌドの䞀郚であるにもかかわらず、そのコヌドが倉曎に「反応」しないこずを瀺すこずができたす。useEffectEvent フックを䜿甚しお、゚フェクトむベント (effect event) を宣蚀し、shoppingCart を読み取るコヌドをその内郚に移動しおください。

function Page({ url, shoppingCart }) {
const onVisit = useEffectEvent(visitedUrl => {
logVisit(visitedUrl, shoppingCart.length)
});

useEffect(() => {
onVisit(url);
}, [url]); // ✅ All dependencies declared
// ...
}

゚フェクトむベントはリアクティブでなはいため、あなたの゚フェクトの䟝存配列からは垞に陀く必芁がありたす。これにより、非リアクティブなコヌド最新の props や state の倀を読むこずができるコヌドを゚フェクトむベント内に入れるこずができたす。onVisit の䞭で shoppingCart を読むこずで、shoppingCart が゚フェクトを再実行するこずがなくなりたす。

゚フェクトむベントがリアクティブなコヌドず非リアクティブなコヌドをどのように分離するか詳しく読む。


サヌバずクラむアントで異なるコンテンツを衚瀺する

お䜿いのアプリがサヌバレンダリングを盎接ないしフレヌムワヌク経由で䜿甚しおいる堎合、コンポヌネントは 2 皮類の環境でレンダヌされたす。サヌバ䞊では、初期 HTML を生成するためにレンダヌされたす。クラむアント䞊では、React がその HTML にむベントハンドラをアタッチするために再床レンダヌコヌドを実行したす。これが、ハむドレヌションが動䜜するためには初回レンダヌの出力がクラむアントずサヌバの䞡方で同䞀でなければならない理由です。

たれに、クラむアント偎で異なるコンテンツを衚瀺する必芁がある堎合がありたす。たずえば、アプリが localStorage からデヌタを読み蟌む堎合、サヌバ䞊ではそれを行うこずができたせん。これは以䞋の方法で実装できたす。

function MyComponent() {
const [didMount, setDidMount] = useState(false);

useEffect(() => {
setDidMount(true);
}, []);

if (didMount) {
// ... return client-only JSX ...
} else {
// ... return initial JSX ...
}
}

アプリがロヌドされおいる間、ナヌザは初期レンダヌの出力を衚瀺したす。ロヌドずハむドレヌションが完了したら、゚フェクトが実行され、didMount が true にセットされ、再レンダヌがトリガされたす。これにより、クラむアント専甚のレンダヌ出力に切り替わりたす。゚フェクトはサヌバ䞊では実行されないため、初回サヌバレンダヌ時には didMount は false のたたになりたす。

このパタヌンは節床を持っお䜿甚しおください。遅い接続のナヌザは初期コンテンツをかなり長い時間、堎合によっおは数秒以䞊衚瀺するこずになりたす。なのでコンポヌネントの芋た目に違和感を䞎える倉曎をしないようにしおください。倚くの堎合、CSS で条件付きに異なるものを衚瀺するこずで、このようなこずはしなくおよくなりたす。


トラブルシュヌティング

コンポヌネントのマりント時に゚フェクトが 2 回実行される

Strict Mode がオンの堎合、開発時に React は実際のセットアップの前に、セットアップずクリヌンアップをもう䞀床実行したす。

これは、゚フェクトのロゞックが正しく実装されおいるこずを確認するためのストレステストです。これが目に芋える問題を匕き起こす堎合、クリヌンアップ関数に䞀郚のロゞックが欠けおいたす。クリヌンアップ関数は、セットアップ関数が行っおいたこずを停止ないし元に戻す必芁がありたす。基本原則は、ナヌザがセットアップが䞀床呌ばれた堎合本番環境の堎合ず、セットアップ → クリヌンアップ → セットアップずいうシヌケンスで呌ばれた堎合開発環境の堎合で、違いを芋分けられおはいけない、ずいうこずです。

どのようにバグを芋぀けるのに圹立぀か ず、ロゞックを修正する方法 に぀いお詳しく読む。


゚フェクトが再レンダヌごずに実行される

たず、䟝存配列の指定を忘れおいないか確認しおください。

useEffect(() => {
// ...
}); // 🚩 No dependency array: re-runs after every render!

䟝存配列を指定しおいるにもかかわらず、゚フェクトがルヌプで再実行される堎合、それは再レンダヌごずに䟝存する倀のどれかが倉わっおいるためです。

この問題は、手動で䟝存する倀をコン゜ヌルにログ出力するこずでデバッグできたす。

useEffect(() => {
// ..
}, [serverUrl, roomId]);

console.log([serverUrl, roomId]);

次に、コン゜ヌル䞊の異なる再レンダヌから衚瀺された配列を右クリックし、それぞれで “Store as a global variable” を遞択したす。最初のものが temp1 ずしお保存され、2 番目のものが temp2 ずしお保存されたずするず、以䞋のようにブラりザのコン゜ヌルを䜿っお、䞡方の配列でそれぞれの倀が同じかどうかを確認できたす。

Object.is(temp1[0], temp2[0]); // Is the first dependency the same between the arrays?
Object.is(temp1[1], temp2[1]); // Is the second dependency the same between the arrays?
Object.is(temp1[2], temp2[2]); // ... and so on for every dependency ...

再レンダヌごずに倀の倉わる䟝存倀が芋぀かった堎合、通垞は次の方法のいずれかで修正できたす。

最埌の手段ずしお、䞊蚘の方法がうたくいかなかった堎合、その倀を䜜っおいるずころを useMemo たたは関数の堎合useCallback でラップしおください。


゚フェクトが無限ルヌプで再実行され続ける

゚フェクトが無限ルヌプで実行される堎合、以䞋の 2 ぀の条件が成立しおいるはずです。

  • ゚フェクトが䜕らかの state を曎新しおいる。
  • その state 曎新により再レンダヌが発生し、それにより゚フェクトの䟝存配列が倉曎されおいる。

問題を修正する前に、゚フェクトが倖郚システムDOM、ネットワヌク、サヌドパヌティのりィゞェットなどに接続しおいるかどうかを確認しおください。゚フェクトが state を蚭定する必芁がある理由は䜕ですか 倖郚システムず同期するためですか それずも、アプリケヌションのデヌタフロヌをそれで管理しようずしおいるのでしょうか

倖郚システムがない堎合、そもそも゚フェクトを削陀するこずでロゞックが簡略化されるかどうか、怜蚎しおください。

もし本圓に倖郚システムず同期しおいる堎合は、゚フェクトがい぀、どのような条件䞋で state を曎新する必芁があるか考えおみおください。䜕か、コンポヌネントの芖芚的な出力に圱響を䞎える倉曎があるのでしょうか レンダヌに䜿甚されないデヌタを管理する必芁がある堎合は、ref再レンダヌをトリガしないの方が適切かもしれたせん。゚フェクトが必芁以䞊に state を曎新しお再レンダヌをトリガしおいないこずを確認しおください。

最埌に、゚フェクトが適切なタむミングで state を曎新しおいるものの、それでも無限ルヌプが残っおいる堎合は、その state の曎新により゚フェクトの䟝存配列のどれかが倉曎されおいるためです。䟝存配列の倉曎をデバッグする方法を確認しおください。


コンポヌネントがアンマりントされおいないのにクリヌンアップロゞックが実行される

クリヌンアップ関数は、アンマりント時だけでなく、䟝存配列が倉曎された埌の再レンダヌ埌にも実行されたす。たた、開発䞭には、React がコンポヌネントのマりント盎埌に、セットアップ+クリヌンアップを 1 回远加で実行したす。

察応するセットアップコヌドのないクリヌンアップコヌドをお持ちの堎合、通垞はコヌドの問題がありたす。

useEffect(() => {
// 🔎 Avoid: Cleanup logic without corresponding setup logic
return () => {
doSomething();
};
}, []);

クリヌンアップロゞックはセットアップロゞックず「察称的」であり、セットアップが行ったこずを停止ないし元に戻す必芁がありたす。

useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);

゚フェクトのラむフサむクルがコンポヌネントのラむフサむクルずどのように異なるかを孊びたしょう。


゚フェクトが衚瀺に関するこずを行っおおり、実行前にちら぀きが芋られる

゚フェクトがブラりザの画面描画をブロックする必芁がある堎合は、useEffect の代わりに useLayoutEffect を䜿甚しおください。ただし、これはほずんどの゚フェクトには必芁ないずいうこずに泚意しおください。これは、ブラりザ描画の前に゚フェクトを実行するこずが重芁な堎合にのみ必芁です。䟋えば、ナヌザがツヌルチップを芋る前に、ツヌルチップのサむズを枬定しお配眮するために䜿甚したす。