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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- ReactアプリでCore Web Vitalsを測定
- Bundle分析とCode Splitting戦略
- ReactでのImageとFontの最適化
- INPの改善:Event Handlerの最適化