hk_1721
@hk_1721 (kehi)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Reactの関数コンポーネントに書いた処理とuseEffectフックの違いについて

解決したいこと

Reactの関数コンポーネントについて質問です。
useEffectフックの第2引数に何も渡さない場合、再レンダリングの度に処理が実行されると思いますが、これは関数コンポーネントの中に書いた処理と何か違いはあるのでしょうか?
以下のソースで試してみたところ、initializeとupdateは初回のレンダリング時やstateの更新時など同じタイミングでコンソールに出力されました。
もし違いがない場合、どちらに書く方がベターなのか(可読性なども含め)、ご教授ください。
よろしくお願いします。

該当するソースコード

const Sample = () => {
  const [num, setNum] = useState(0);
  console.log("initialize");
  useEffect(() => {
    console.log("update");
  });
  return (
    <button onClick={()=>{setNum(num+1)}}>plus</button>
  )
}
export default Sample;
1

2Answer

useEffectにはレンダリング時の処理以外に,コールバックが関数を返すことにより,その処理をクリーンアップとして実行するという仕様があります.
具体的には,setIntervalを使いたい場合に,二重にintervalが走るのを防ぐために,useEffectを使用することでスマートに実装することができます.

このようにuseEffectはレンダリング時に必ず処理を実行したい場合でも明確に用途が分けられているため,クリーンアップが必要な処理をコンポーネントにベタ書きしたり,逆にクリーンアップが必要無い処理をむやみにuseEffectで書くべきではありません.
もちろん特定の変数のみに依存して処理を行いたい場合は,useEffect等を使用してパフォーマンスを損なわないようにする必要があります.

0Like

Comments

  1. @hk_1721

    Questioner

    クリーンアップの有無で切り分けるというのは、認識していませんでした。

    とても勉強になりました。
    ありがとうございます。

参考記事

ものすごく雑な理解なので恐縮ですが、useEffect はその実行タイミングから、以下のような場合のために設計されていると考えています。

  • 「待ち合わせが発生する」処理をレンダリング完了後に先送りするため
    • いったん空の画面を表示 → 裏で非同期処理を走らせる → 遅れて更新する
  • 描画した結果の実際のDOMに対してあれこれするために
    • React の「レンダリング」中は実 DOM が未更新なので、コードから見ると過去を操作することになっておかしくなります。
    • (注意)ブラウザのレンダリングと React のレンダリングは違います。

具体的に、私が思いつく限りで useEffect で書かなければいけないものは、以下ぐらいだと思います。

これら以外のコードは、「再レンダリングのときに(useEffectを使わず)そのまま式を書くだけで済む」と考えてよいと思います。


  • useEffect を使うとワンテンポ遅れて呼び出される
  • Aが変更したときにBが変更されて、Bの変更に応じて ... とコードが複雑化しすぎる原因になる

ので、 使わなくて良いときには useEffect を避けるのが良いと思います。

再レンダリングしたことを確認するため(あるいは状態をコンソールに出力するため)に console.log を使うだけなら直に書いてしまってよいと思います。

const [age, setAge] = useState(0);
console.log(age); // 再レンダリングのたびに age の中身を出力する

useEffect が使われがちなのはたいてい「A という状態が変更したときに B を更新する」といったケースだと思いますが、下記のような書き方ができます。 React のメカニズムに合致していて、書き方を覚えてしまえば読みやすいコードにできます。

const [familyName, setFamilylName] = useState("");
const [personalName, setPersonalName] = useState("");

// const [fullName, setFullName] = useState(""); // これは不要

// fullName は再レンダリングのたびに再計算される
const fullName = familyName + " " + personalName;
0Like

Comments

  1. @hk_1721

    Questioner

    必要な場合だけuseEffectを使う、ということで疑問に思っていたことが解決できました。
    とても丁寧にご回答いただき、ありがとうございました。

Your answer might help someone💌