0Pricing
React Academy · レッスン

INPの改善:Event Handlerの最適化

負荷の高いhandlersをdebounceし、scheduler.yield()を使い、main threadをブロックするlong tasksを避けます。

「INPの改善:Event Handlerの最適化」はCoddyKit上の無料React Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはReact Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 React Academyコースには全4レッスンが含まれています。

INP とは

Interaction to Next Paint(INP)は、ユーザーの操作(クリック、タップ、キー入力)から、ブラウザがその応答を描画するまでの遅延を測定します。INP が 200ms を超えると、動作が遅く感じられます。

React のイベントが遅くなる理由

処理を同期的に行いすぎる React のイベントハンドラーは、メインスレッドをブロックします。JavaScript のタスクが完了するまでブラウザは応答を描画できないため、INP が高くなります。

入力のデバウンス

キーストロークによって実行される負荷の高い処理(検索 API の呼び出しや複雑な計算)をデバウンスします。キーストロークごとの React の state 更新は問題ありませんが、負荷の高い処理はデバウンスする必要があります。

import { useDeferredValue, useState } from 'react';

function SearchPage() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query); // defers re-render of slow parts

  return (
    <>
      <input value={query} onChange={e => setQuery(e.target.value)} />
      <SearchResults query={deferredQuery} /> {/* re-renders with lower priority */}
    </>
  );
}

緊急性の低い更新に useTransition を使う

遅い state 更新をstartTransitionで囲み、緊急性が低い処理として扱います。React が処理を遅延させるため、入力の応答性を維持できます。

const [isPending, startTransition] = useTransition();

function handleFilter(value) {
  setInputValue(value); // urgent — update input immediately
  startTransition(() => {
    setFilteredList(expensiveFilter(value)); // non-urgent — can be interrupted
  });
}

負荷の高い処理をメインスレッドから移す

CPU 負荷の高いタスク(大規模データセットの解析や画像処理)には Web Worker を使います。Worker が計算している間も、メインスレッドは応答可能な状態を保てます。

const worker = new Worker(new URL('./heavy-worker.js', import.meta.url));

function processData(data) {
  return new Promise(resolve => {
    worker.postMessage(data);
    worker.onmessage = e => resolve(e.data);
  });
}

// In component:
const handleClick = async () => {
  const result = await processData(largeDataset); // doesn't block main thread
  setResult(result);
};

長いタスクを避ける

長いタスク(>50ms)はメインスレッドをブロックし、INP を悪化させます。setTimeout、scheduler.yield()、またはrequestIdleCallbackを使って処理を分割します。

async function processInChunks(items) {
  const CHUNK_SIZE = 100;
  for (let i = 0; i < items.length; i += CHUNK_SIZE) {
    const chunk = items.slice(i, i + CHUNK_SIZE);
    processChunk(chunk);
    await new Promise(r => setTimeout(r, 0)); // yield to browser between chunks
  }
}

scheduler.yield()(最新の API)

scheduler.yield()はブラウザのイベントループに制御を返し、コードの実行を再開する前に保留中のユーザー操作を処理できるようにします。

async function longTask() {
  for (const item of largeList) {
    processItem(item);
    if (/* every N items */) {
      await scheduler.yield(); // let browser handle pending clicks/inputs
    }
  }
}

ハンドラー内で同期的なレイアウトを避ける

レイアウトプロパティ(offsetWidth、getBoundingClientRect())を読み取ると、同期的なレイアウト再計算が強制されます。レイアウトスラッシングを避けるため、読み取りを先にまとめてから書き込みます。

// Bad — forces layout recalculation on each iteration:
items.forEach(item => {
  const height = item.offsetHeight; // READ — forces layout
  item.style.height = height * 2 + 'px'; // WRITE — dirties layout
});

// Good — batch reads, then writes:
const heights = items.map(item => item.offsetHeight); // all reads
heights.forEach((h, i) => items[i].style.height = h * 2 + 'px'); // all writes

非同期 INP のための React 19 Actions

React 19 Actions は UI をブロックせずに非同期ミューテーションを処理します。保留中の state 更新が独立して行われるため、ページの操作性が維持されます。

function LikeButton({ postId }) {
  const [optimistic, setOptimistic] = useOptimistic(false);

  async function handleLike() {
    setOptimistic(true);
    await likePost(postId); // async — doesn't block
  }

  return <button onClick={handleLike}>{optimistic ? 'Liked' : 'Like'}</button>;
}

イベント委譲の注意点

React は合成イベントの委譲を使い、ルートに1つのリスナーをアタッチします。useEffectで個別の DOM リスナーを何百個もアタッチするのは避け、代わりに JSX 要素上で React の合成イベントを使います。

DevTools で INP を測定する

Chrome DevTools の Performance パネルを使ってインタラクションを記録し、長いタスクを調べます。タイムラインで「Interaction」エントリを探し、展開してブロックしているスクリプトの評価を確認します。

確認問題

ユーザー操作によって中断できるように、state 更新を緊急性の低い処理として扱う React フックはどれですか?

まとめ

負荷の高い処理をデバウンスし、緊急性の低い更新にはstartTransitionを使い、CPU 負荷の高い処理を Web Worker に移し、scheduler.yield()で長いタスクを分割することで INP を改善します。レイアウトスラッシングを避けるため、DOM の読み取りを先にまとめてから書き込みます。

よくある質問

「INPの改善:Event Handlerの最適化」レッスンは無料ですか?

はい。「INPの改善:Event Handlerの最適化」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、React Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 React Academyコースには全4レッスンが含まれています。

「INPの改善:Event Handlerの最適化」で何を学びますか?

負荷の高いhandlersをdebounceし、scheduler.yield()を使い、main threadをブロックするlong tasksを避けます。 ブラウザで直接実行するハンズオンコードでReact Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

React Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのReact Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「INPの改善:Event Handlerの最適化」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このReact Academyレッスンでコードを書いて実行できますか?

はい。すべてのReact Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. ReactアプリでCore Web Vitalsを測定
  2. Bundle分析とCode Splitting戦略
  3. ReactでのImageとFontの最適化
  4. INPの改善:Event Handlerの最適化
← React Academyに戻る