Skip to content

Instantly share code, notes, and snippets.

@LeeDDHH
Last active September 24, 2022 07:12
Show Gist options
  • Save LeeDDHH/640b5ee0467bd4afcb76870cc570324d to your computer and use it in GitHub Desktop.
Save LeeDDHH/640b5ee0467bd4afcb76870cc570324d to your computer and use it in GitHub Desktop.
Reactで開発しながら疑問に思ってたこと

Class Components

非同期通信


クリップボードにコピーする

onClick={() => {navigator.clipboard.writeText(this.state.textToCopy)}}

参考


'React' は UMD グローバルを参照していますが、現在のファイルはモジュールです。代わりにインポートを追加することを考慮してください。 ts(2686)

  • import React from 'react'; をする

参考


react strict modeでuseEffectが2回実行する

  • Reactのv18からStrictモードに挙動が追加される
  • Strictモード
    • アプリケーションに存在する様々な潜在的な問題点を警告で出してくれるもの
    • Strictモードによって影響があるのは開発モードのみ
    • 本番環境ビルドでは何も影響がない
    • 予期せぬ副作用の検出をする
  • ReactがUIを表示するまでの過程
    • レンダリングフェーズ
      • DOMにどのような変更を加えるかを決めるステップ
        • 差分比較
        • render処理、setStateによる更新処理など
    • コミットフェーズ
      • 変更を実際に加えるフェーズ
        • useEffect処理など
  • React 17の世界
    • レンダリングフェーズとコミットフェーズが1回ずつ行われる
  • React 18以降の世界
    • concurrent renderingによって、レンダリングフェーズが複数回行われる
    • レンダリングフェーズで冪等(べきとう)ではない副作用の処理を行っていた場合、意図しない挙動を行うことになる
  • 予期しない副作用が起こるケース
    • そもそも表面化しなくて見つけにくなったり、再現するのが難しかったりする
    • このような挙動を検出しやすくするのがStrictモード
    • しかし、Strictモードを使っても予期しない副作用を自動的に検出することはできない
    • その代わりに開発モードにおいて、以下の関数を意図的に2回呼び出すようにしている
      • Class component constructor, render, and shouldComponentUpdate methods
      • Class component static getDerivedStateFromProps method
      • Function component bodies
      • State updater functions (the first argument to setState)
      • Functions passed to useState, useMemo, or useReducer
      • doc
    • 関数内で冪等じゃない処理を行なっていた場合は、2回呼び出されることによって問題が表面化しやすくなる
      • 問題のある処理を発見しやすくするための仕組みがStrictモード
  • React18で追加された挙動
    • Strictモード
      • 上記の挙動に併せて、初回レンダリング時にmount→unmount→mount という挙動を行うようになった
      • 開発モード
        • 初回レンダリングでmountとunmountを繰り返す
        • useEffectの第二引数を[]にしていても処理が2回走るようになる
      • useEffectの中でクリーンアップ関数込みで冪等(べきとう)でない副作用の処理をしていた場合はバグが起きる可能性がある
    • 現時点では、concurrent renderingでもコミットフェーズはレンダリングの過程で1回しか呼ばれない
  • Offscreen API
    • タブのようなUIでstateを管理するためのもの
    • タブの中でstate管理していた場合
      • 通常は別タブをクリックした時点で元のタブはunmountされるので、保持していたstateはリセットされる
      • 元のタブに戻った際にはstateは初期化された状態になる
    • 元のタブに戻った時に、以前までのstateを保持していたいよねってのを実現するためのもの
      • unmountはせずあくまでUI上隠しておくに留める
      • state自体は保持しておいて、再度表示される際には元のstateを使用する
      • Reactはunmountのクリーンアップ関数処理を行うことを通じて、隠すという動作をコンポーネントに伝える
      • 再表示の際にはmount時に発火する処理と同じ処理を行う
      • useEffectはこれまでmountで発火してunmountでクリーンアップという挙動だったが、Offscreen API下では表示で発火して非表示でクリーンアップという挙動になる
      • useEffectの処理がクリーンアップ込みで冪等(べきとう)ではない場合意図しない副作用が発生することになる
      • このような実装を検知しやすくするためにStrictモードではmount →unmount→mountという挙動を追加した

実際に1回しか発火させたくない場合

  • refを使って管理する
const didLogRef = useRef(false);

useEffect(() => {
  // In this case, whether we are mounting or remounting,
  // we use a ref so that we only log an impression once.
  if (didLogRef.current === false) {
    didLogRef.current = true;

    SomeTrackingAPI.logImpression();
  }
}, []);
  • 1つのhooksにまとめて関数を引数として渡して使う
import { useEffect, useRef } from "react";

export const useOneTimeMountEffect = (functionOnMount:() => void) => {
    const didLogRef = useRef(false);

    return useEffect(() => {
        if(didLogRef.current === false){
            didLogRef.current = true;
            functionOnMount();
        }
    },[]);
} 

参考

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment