カランのブログ

Kalan 頭像照片,在淡水拍攝,淺藍背景

四零二曜日電子報上線啦!訂閱訂起來

ソフトウェアエンジニア / 台湾人 / 福岡生活
このブログはRSS Feed をサポートしています。RSSリンクをクリックして設定してください。技術に関する記事はコードがあるのでブログで閲覧することをお勧めします。

今のモード ライト

我會把一些不成文的筆記或是最近的生活雜感放在短筆記,如果有興趣的話可以來看看唷!

記事のタイトルや概要は自動翻訳であるため(中身は翻訳されてない場合が多い)、変な言葉が出たり、意味伝わらない場合がございます。空いてる時間で翻訳します。

React 17 アップデートフォーカス-useEffect のクリーンアップ関数のタイミング変更

(このスクリーンショットはReact公式ブログから取得されました)

React 17はすでにRC段階に入っています。公式ブログの声明によると、今回のリリースには重大な更新や新機能はありませんが、記事の中には興味深い情報がいくつかありますので、ここで紹介します。

この記事では、すべてのアップデートを一つ一つ説明するのではなく、注目に値する部分を記録します。

イベントデリゲーションのノードの変更

Reactでは、<button onClick={}>のようなイベントリスナーを使用する場合、Reactはすべてのイベントリスナーをノード自体ではなくdocumentに配置します。これはイベントデリゲーションと呼ばれるテクニックで、多数のイベントリスナーによるパフォーマンスの問題を回避するためのものです。React 17では、リスナーはドキュメントではなくルートエレメントに配置されます。これはReactが制御下にあるため、将来的には「Replay Event」といった新機能を簡単に追加できるという利点があります。

イベントプーリングメカニズムの削除

パフォーマンスの向上のため、React 17以前では、イベントプーリングメカニズムが使用されていました。これは、独自の「Synthetic Event」という仕組みを実装していました。しかし、公式の説明によると、現代のブラウザでは、その効果は限定的であり、開発者に混乱を引き起こすこともあります。たとえば、e.targetを他のコンポーネントにパラメータとして渡す場合、nullであることに気づくかもしれません。これは、Reactがパフォーマンスを向上させるために行った設計です。正しく使用するには、e.persist()を追加してReactがそれに干渉しないようにする必要があります。

React 17以降、イベントプーリングメカニズムは完全に削除されましたので、e.persist()を追加する必要はありません。素晴らしいですね。一部の記事やチュートリアルでは、Reactのイベントプーリング設計が強調されているかもしれませんが、将来的にはその説明を変更する必要があるかもしれません。

星を打つ:useEffectのクリーンアップ関数のタイミング変更

これはおそらく今回の変更の中で最も重要なものです。

useEffectを使用する場合、ほとんどのeffectは画面の更新とは関係ありませんので、Reactは画面の更新の後effectを実行します。

全体的なフローは以下のようになります:

コンポーネントの更新 → DOMの対応する変更 → 画面の更新 → effectの実行

これにより、effect内で行われる処理が計算量が大きい場合にDOMの描画に影響を与えるのを避けることができます。例えば:

const App = () => {
  useEffect(() => {
    longlongTask(); // この処理は長い時間がかかります
    console.log('Hello World');
  })
  return <Component />
}

上記のフローにより、Reactは画面の更新後に他のeffectを実行することで、ユーザーエクスペリエンスを損なうことを防ぎます。

ただし、画面のジャンプを防ぐために画面の更新前にeffectを実行したい場合は、useLayoutEffectを使用することができます。

以上がReact 16の予想される動作です。次に、クリーンアップの部分について説明します。

React 16では、useEffecteffectは画面の更新後に実行されますが、クリーンアップ関数は画面の更新の前に実行されます。一般的には、これには影響はありません。ほとんどのクリーンアップ関数は、単にunsubscribe、リスナーの解除、またはAPIのキャンセルなどの処理です。ただし、クリーンアップ関数が多くの時間を占有する場合、画面の更新に影響を与える可能性があります。

次のように書いた場合を想定してみましょう:

const App = () => {
  useEffect(() => {
    longlongTask(); // この処理は長い時間がかかりますが、心配しないでください、Reactは画面の更新後に実行します
    console.log('Hello World');
    return () => {
      longlongTask(); // 画面がlonglongTask()の終了までフリーズします(React 16では)
    }
  })
  return <Component />
}

React 16では、クリーンアップの実行タイミングは次のようになります(コンポーネントの更新時):

コンポーネントの更新 → DOMの対応する変更 → クリーンアップ関数の実行 → 画面の更新 → effectの実行

一方、React 17以降では、クリーンアップのタイミングは画面の更新の後に変更されました(コンポーネントの更新時):

コンポーネントの更新 → DOMの対応する変更 → 画面の更新 → クリーンアップ関数の実行effectの実行

コンポーネントがアンマウントされる場合:

React 16:コンポーネントの更新 → クリーンアップ関数の実行 → DOMの対応する変更 → 画面の更新

React 17:コンポーネントの更新 → DOMの対応する変更 → 画面の更新 → クリーンアップ関数の実行

クリーンアップ関数はuseEffectの順序に従って実行されます

React 17では、クリーンアップ関数はツリー内の位置に応じて、効果の順序と同じ順序で実行されます。以前は、この順序が時折異なることがありました。

クリーンアップ関数に順序依存性が必要な場合はわかりませんが、React 16では、クリーンアップの実行タイミングが予期しない順序で発生することがありました。

次の記事

ArduinoとESP32を使った大気質モニタリングアプリケーションの作成 (4)-WiFi

前の記事

クッキーと CORS についての再考

この文章が役に立つと思うなら、下のリンクで応援してくれると大変嬉しいです✨

Buy me a coffee