useDeferredValue は、UI οΏ½?一部�?ζ›΄ζ–°γ‚’ι…ε»Άγ•γ›γ‚‹γŸγ‚οΏ½? React フックです。

const deferredValue = useDeferredValue(value)

γƒͺフゑレンス

useDeferredValue(value)

γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?γƒˆγƒƒγƒ—γƒ¬γƒ™γƒ«γ§ useDeferredValue を呼び出し、そ�?ε€€οΏ½?ι…ε»Άγ•γ‚ŒγŸγƒγƒΌγ‚Έγƒ§γƒ³γ‚’ε–εΎ—γ—γΎγ™γ€‚

import { useState, useDeferredValue } from 'react';

function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
// ...
}

さらに例を見る

εΌ•ζ•°

  • value: ι…ε»Άγ•γ›γŸγ„ε€€γ€‚δ»»ζ„οΏ½?εž‹γ‚’ζŒγ€γ“γ¨γŒγ§γγΎγ™γ€‚

θΏ”γ‚Šε€€

εˆε›žγƒ¬γƒ³γƒ€γƒΌζ™‚γ«γ―γ€θΏ”γ•γ‚Œγ‚‹ε€€γ―γ‚γͺγŸγŒζΈ‘γ—γŸε€€γ¨εŒδΈ€γ«γͺγ‚ŠγΎγ™γ€‚ζ›΄ζ–°ζ™‚γ«γ―γ€React γ―γΎγšε€γ„ε€€γ§ε†γƒ¬γƒ³γƒ€γƒΌγ‚’θ©¦γΏοΌˆγ€γΎγ‚ŠθΏ”γ‚Šε€€γ―ε€γ„ε€€γ«γͺγ‚ŠοΌ‰γ€ζ¬‘γ«ζ–°γ—γ„ε€€γ§γƒγƒƒγ‚―γ‚°γƒ©γ‚¦γƒ³γƒ‰γ§ε†γƒ¬γƒ³γƒ€γƒΌγ‚’θ©¦γΏγΎγ™οΌˆθΏ”γ‚Šε€€γ―ζ›΄ζ–°εΎŒοΏ½?倀にγͺγ‚ŠγΎγ™οΌ‰γ€‚

注意点

  • useDeferredValue に渑す倀は、プγƒͺγƒŸγƒ†γ‚£γƒ–γͺε€€οΌˆζ–‡ε­—εˆ—γ‚„ζ•°ε€€γͺγ©οΌ‰γΎγŸγ―γƒ¬γƒ³γƒ€γƒΌοΏ½?ε€–ιƒ¨γ§δ½œζˆγ•γ‚ŒγŸγ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ§γ‚γ‚‹γΉγγ§γ™γ€‚γƒ¬γƒ³γƒ€γƒΌδΈ­γ«ζ–°γ—γ„γ‚ͺγƒ–γ‚Έγ‚§γ‚―γƒˆγ‚’δ½œζˆγ—γ¦γ™γγ«γγ‚Œγ‚’ useDeferredValue γ«ζΈ‘γ™γ¨γ€γγ‚Œγ―ζ―Žε›žοΏ½?レンダーで異γͺγ‚‹γ‚‚οΏ½?とγͺγ‚‹γŸγ‚γ€δΈεΏ…θ¦γͺバックグラウンドで�?再レンダーを引き衷こします。

  • useDeferredValue が(Object.is で比較して)異γͺγ‚‹ε€€γ‚’ε—γ‘ε–γ‚‹γ¨γ€οΌˆε‰ε›žοΏ½?ε€€γ‚’δ½Ώη”¨γ™γ‚‹οΌ‰ηΎεœ¨οΏ½?γƒ¬γƒ³γƒ€γƒΌγ«εŠ γˆγ¦γ€ζ–°γ—γ„ε€€γ§γƒγƒƒγ‚―γ‚°γƒ©γ‚¦γƒ³γƒ‰γ§ε†γƒ¬γƒ³γƒ€γƒΌγ‚’γ‚Ήγ‚±γ‚Έγƒ₯ールします。バックグラウンドで�?再レンダーは中断可能です。value にεˆ₯οΏ½?ζ›΄ζ–°γŒγ‚γ‚‹γ¨γ€React はバックグラウンドで�?ε†γƒ¬γƒ³γƒ€γƒΌγ‚’ζœ€εˆγ‹γ‚‰γ‚„γ‚Šη›΄γ—γΎγ™γ€‚δΎ‹γˆγ°γ€γƒ¦γƒΌγ‚ΆγŒη΄ ζ—©γε…₯εŠ›γ‚’θ‘Œγ„γ€γγ‚ŒγŒγοΏ½?ε€€γ‚’ε—γ‘ε–γ‚‹γƒγƒ£γƒΌγƒˆγ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγŒε†γƒ¬γƒ³γƒ€γƒΌγ§γγ‚‹γ‚ˆγ‚Šγ‚‚ι€Ÿγ‹γ£γŸε ΄εˆγ€γƒγƒ£γƒΌγƒˆγ―γƒ¦γƒΌγ‚ΆγŒγ‚Ώγ‚€γƒ—γ‚’ζ­’γ‚γŸγ‚γ¨γ«ε†θ‘¨η€Ίγ•γ‚Œγ‚‹γ“γ¨γ«γͺγ‚ŠγΎγ™γ€‚

  • useDeferredValue は <Suspense> γ¨η΅±εˆγ•γ‚Œγ¦γ„γΎγ™γ€‚ζ–°γ—γ„ε€€γ«γ‚ˆγ£γ¦εΌ•γθ΅·γ“γ•γ‚Œγ‚‹γƒγƒƒγ‚―γ‚°γƒ©γ‚¦γƒ³γƒ‰ζ›΄ζ–°γŒ UI γ‚’γ‚΅γ‚Ήγƒšγƒ³γƒ‰γ—γŸε ΄εˆγ§γ‚‚γ€γƒ¦γƒΌγ‚Άγ«γƒ•γ‚©γƒΌγƒ«γƒγƒƒγ‚―γ―θ‘¨η€Ίγ•γ‚ŒγΎγ›γ‚“γ€‚γƒ‡γƒΌγ‚ΏγŒθͺ­γΏθΎΌγΎγ‚Œγ‚‹γΎγ§γ€δ»₯前�?ι…ε»Άγ•γ‚ŒγŸε€€γŒθ‘¨η€Ίγ•γ‚ŒηΆšγ‘γΎγ™γ€‚

  • useDeferredValue θ‡ͺδ½“γ«δ½™θ¨ˆγͺγƒγƒƒγƒˆγƒ―γƒΌγ‚―γƒͺγ‚―γ‚¨γ‚Ήγƒˆγ‚’ι˜²γδ»•η΅„γΏγ―γ‚γ‚ŠγΎγ›γ‚“γ€‚

  • useDeferredValue θ‡ͺδ½“γ«γ‚ˆγ‚‹ε›ΊοΏ½?οΏ½οΏ½?ι…ε»Άγ―γ‚γ‚ŠγΎγ›γ‚“γ€‚React γŒε…ƒοΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγ‚’η΅‚γˆγ‚‹γ¨γ™γγ«γ€ζ–°γ—γ„ι…ε»Άε€€γ§οΏ½?γƒγƒƒγ‚―γ‚°γƒ©γ‚¦γƒ³γƒ‰ε†γƒ¬γƒ³γƒ€γƒΌδ½œζ₯­γ‚’ι–‹ε§‹γ—γΎγ™γ€‚γ‚€γƒ™γƒ³γƒˆοΌˆγ‚Ώγ‚€γƒ”γƒ³γ‚°γͺγ©οΌ‰γ«γ‚ˆγ‚‹ζ›΄ζ–°γ―γ€γƒγƒƒγ‚―γ‚°γƒ©γ‚¦γƒ³γƒ‰οΏ½?再レンダーを中断してε„ͺε…ˆηš„γ«ε‡¦η†γ•γ‚ŒγΎγ™γ€‚

  • useDeferredValue γ«γ‚ˆγ‚‹γƒγƒƒγ‚―γ‚°γƒ©γ‚¦γƒ³γƒ‰οΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγ―γ€η”»ι’γ«γ‚³γƒŸγƒƒγƒˆγ•γ‚Œγ‚‹γΎγ§γ‚¨γƒ•γ‚§γ‚―γƒˆγ‚’οΏ½?οΏ½θ‘Œγ—γΎγ›γ‚“γ€‚γƒγƒƒγ‚―γ‚°γƒ©γ‚¦γƒ³γƒ‰οΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγŒγ‚΅γ‚Ήγƒšγƒ³γƒ‰γ™γ‚‹ε ΄εˆγ€γοΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγ«ε―ΎεΏœγ™γ‚‹γ‚¨γƒ•γ‚§γ‚―γƒˆγ―γƒ‡γƒΌγ‚ΏοΏ½?θͺ­γΏθΎΌγΏγ¨ UI οΏ½?ζ›΄ζ–°οΏ½?後に�?οΏ½θ‘Œγ•γ‚ŒγΎγ™γ€‚


使用法

ζ–°γ—γ„γ‚³γƒ³γƒ†γƒ³γƒ„γŒθͺ­γΏθΎΌγΎγ‚Œγ¦γ„る間、叀いコンテンツを葨瀺する

UI οΏ½?一部�?ζ›΄ζ–°γ‚’ι…ε»Άγ•γ›γ‚‹γŸγ‚γ«γ€γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?γƒˆγƒƒγƒ—γƒ¬γƒ™γƒ«γ§ useDeferredValue を呼び出します。

import { useState, useDeferredValue } from 'react';

function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
// ...
}

εˆε›žγƒ¬γƒ³γƒ€γƒΌζ™‚γ«γ―γ€ι…ε»Άγ•γ‚Œγ‚‹ε€€γ―ι–’ζ•°γ«ζΈ‘γ—γŸε€€γ¨εŒγ˜γ«γͺγ‚ŠγΎγ™γ€‚

ζ›΄ζ–°ζ™‚γ«γ―γ€ι…ε»Άγ•γ‚Œγ‚‹ε€€γ―ζœ€ζ–°οΏ½?ε€€γ‹γ‚‰γ€Œι…γ‚Œγ€γΎγ™γ€‚ε…·δ½“ηš„γ«γ―γ€React γ―γΎγšι…ε»Άε€€γ‚’ζ›΄ζ–°γ›γšγ«ε†γƒ¬γƒ³γƒ€γƒΌγ‚’θ‘Œγ„γ€ζ¬‘γ«ζ–°γŸγ«ε—γ‘ε–γ£γŸε€€γ§γƒγƒƒγ‚―γ‚°γƒ©γ‚¦γƒ³γƒ‰γ§οΏ½?再レンダーを試みます。

δΎ‹γ‚’δ½Ώγ£γ¦γ€γ“γ‚ŒγŒε½Ήη«‹γ€ε ΄ι’γ‚’θ¦‹γ¦γ„γγΎγ—γ‚‡γ†γ€‚

補袳

こ�?例では、δ»₯δΈ‹οΏ½?γ‚ˆγ†γͺγ‚΅γ‚Ήγƒšγƒ³γ‚Ή (Suspense) 対応�?データソースを使用していることを前提としています。

  • Relay γ‚„ Next.js οΏ½?γ‚ˆγ†γͺγ‚΅γ‚Ήγƒšγƒ³γ‚Ήε―ΎεΏœοΏ½?フレームワークで�?データフェッチ
  • lazy γ‚’η”¨γ„γŸγ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ‚³γƒΌγƒ‰οΏ½?遅廢ロード
  • use γ‚’η”¨γ„γŸγƒ—γƒ­γƒŸγ‚Ή (Promise) から�?ε€€οΏ½?θͺ­γΏε–γ‚Š

γ‚΅γ‚Ήγƒšγƒ³γ‚Ήγ¨γοΏ½?εˆΆι™γ«γ€γ„γ¦θ©³γ—γε­¦γΆγ€‚

こ�?例では、SearchResults γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆγ―ζ€œη΄’η΅ζžœγ‚’γƒ•γ‚§γƒƒγƒγ™γ‚‹ι–“γ‚΅γ‚Ήγƒšγƒ³γƒ‰γ—γΎγ™γ€‚"a" γ‚’ε…₯εŠ›γ—γ€η΅ζžœγ‚’εΎ…γ£γ¦γ‹γ‚‰ "ab" γ«ζ›Έγζ›γˆγ¦γΏγ¦γγ γ•γ„γ€‚"a" οΏ½?ζ€œη΄’η΅ζžœγŒγ€γƒ­γƒΌγƒ‰δΈ­γƒ•γ‚©γƒΌγƒ«γƒγƒƒγ‚―γ«οΏ½?ζ›γ•γ‚Œγ¦γ—γΎγ„γΎγ™γ€‚

import { Suspense, useState } from 'react';
import SearchResults from './SearchResults.js';

export default function App() {
  const [query, setQuery] = useState('');
  return (
    <>
      <label>
        Search albums:
        <input value={query} onChange={e => setQuery(e.target.value)} />
      </label>
      <Suspense fallback={<h2>Loading...</h2>}>
        <SearchResults query={query} />
      </Suspense>
    </>
  );
}

こ�?δ»£γ‚γ‚Šγ«δΈ€θˆ¬ηš„γ«δ½Ώγ‚γ‚Œγ‚‹ UI γƒ‘γ‚ΏγƒΌγƒ³γ―γ€η΅ζžœγƒͺγ‚ΉγƒˆοΏ½?ζ›΄ζ–°γ‚’ι…ε»Άγ•γ›γ¦γ€ζ–°γ—γ„η΅ζžœγŒζΊ–ε‚™γ§γγ‚‹γΎγ§ε‰οΏ½?η΅ζžœγ‚’θ‘¨η€Ίγ—ηΆšγ‘γ‚‹γ¨γ„γ†γ‚‚οΏ½?です。遅廢バージョン�?クエγƒͺζ–‡ε­—εˆ—γ‚’ζΈ‘γ™γŸγ‚γ« useDeferredValue γ‚’ε‘Όγ³ε‡Ίγ—γΎγ™οΌš

export default function App() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
<>
<label>
Search albums:
<input value={query} onChange={e => setQuery(e.target.value)} />
</label>
<Suspense fallback={<h2>Loading...</h2>}>
<SearchResults query={deferredQuery} />
</Suspense>
</>
);
}

query οΏ½?ζ–Ήγ―γ™γγ«ζ›΄ζ–°γ•γ‚Œγ‚‹γŸγ‚γ€ε…₯εŠ›γƒ•γ‚£γƒΌγƒ«γƒ‰γ―ζ–°γ—γ„ε€€γ‚’θ‘¨η€Ίγ—γΎγ™γ€‚γ—γ‹γ—γ€deferredQuery γ―γƒ‡γƒΌγ‚ΏγŒθͺ­γΏθΎΌγΎγ‚Œγ‚‹γΎγ§ε‰οΏ½?ε€€γ‚’δΏζŒγ™γ‚‹γŸγ‚γ€SearchResults γ―γ—γ°γ‚‰γε€γ„η΅ζžœγ‚’θ‘¨η€Ίγ—γΎγ™γ€‚

δ»₯δΈ‹οΏ½?例で "a" γ‚’ε…₯εŠ›γ—γ€η΅ζžœγŒθͺ­γΏθΎΌγΎγ‚Œγ‚‹οΏ½?を待け、欑にε…₯εŠ›ζ¬„γ‚’ "ab" γ«ζ›Έγζ›γˆγ¦γΏγ¦γγ γ•γ„γ€‚ζ–°γ—γ„η΅ζžœγŒθͺ­γΏθΎΌγΎγ‚Œγ‚‹γΎγ§γ―γ€γ‚΅γ‚Ήγƒšγƒ³γ‚Ήγ«γ‚ˆγ‚‹γƒ•γ‚©γƒΌγƒ«γƒγƒƒγ‚―οΏ½?δ»£γ‚γ‚Šγ«ε€γ„η΅ζžœγƒͺγ‚ΉγƒˆγŒθ‘¨η€Ίγ•γ‚ŒηΆšγ‘γ‚‹γ“γ¨γ«η€οΏ½?してください。

import { Suspense, useState, useDeferredValue } from 'react';
import SearchResults from './SearchResults.js';

export default function App() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);
  return (
    <>
      <label>
        Search albums:
        <input value={query} onChange={e => setQuery(e.target.value)} />
      </label>
      <Suspense fallback={<h2>Loading...</h2>}>
        <SearchResults query={deferredQuery} />
      </Suspense>
    </>
  );
}

さらに深くηŸ₯γ‚‹

ε€€οΏ½?遅廢は内部でど�?γ‚ˆγ†γ«ε‹•δ½œγ™γ‚‹οΏ½?γ‹οΌŸ

ε€€οΏ½?遅廢は 2 ぀�?γ‚Ήγƒ†γƒƒγƒ—γ§θ‘Œγ‚γ‚Œγ‚‹γ¨θ€ƒγˆγ‚‹γ“γ¨γŒγ§γγΎγ™οΌš

  1. まず React は、query は新しい倀 ("ab") だが deferredQuery は叀い倀 ("a") οΏ½?γΎγΎγ€γ¨γ„γ†ηŠΆζ…‹γ§ε†γƒ¬γƒ³γƒ€γƒΌγ‚’θ©¦γΏγΎγ™γ€‚η΅ζžœγƒͺγ‚Ήγƒˆγ«ζΈ‘γ™ε΄οΏ½?倀である deferredQuery γ―ι…ε»Άγ•γ‚Œγ¦γŠγ‚Šγ€query οΏ½?ε€€γ«γ€Œι…γ‚Œγ¦γ€γ€γ„γ¦γ„γγΎγ™γ€‚

  2. バックグラウンドで React は、query と deferredQuery οΏ½?δΈ‘ζ–ΉγŒ "ab" γ«ζ›΄ζ–°γ•γ‚ŒγŸηŠΆζ…‹γ§ε†γƒ¬γƒ³γƒ€γƒΌγ‚’θ©¦γΏγΎγ™γ€‚γ“οΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγŒοΏ½?οΏ½δΊ†γ—γŸε ΄εˆγ€React γ―γγ‚Œγ‚’η”»ι’γ«θ‘¨η€Ίγ—γΎγ™γ€‚γ—γ‹γ—γ€γγ‚ŒγŒγ‚΅γ‚Ήγƒšγƒ³γƒ‰γ—γŸοΌˆ"ab" οΏ½?硐果がまだθͺ­γΏθΎΌγΎγ‚Œγ¦γ„γͺγ„οΌ‰ε ΄εˆγ€React はこ�?レンダー�?θ©¦θ‘Œγ‚’ζ”Ύζ£„γ—γ€γƒ‡γƒΌγ‚ΏγŒθͺ­γΏθΎΌγΎγ‚ŒγŸεΎŒγ«γ“οΏ½?ε†γƒ¬γƒ³γƒ€γƒΌγ‚’ε†θ©¦θ‘Œγ—γΎγ™γ€‚γƒ¦γƒΌγ‚Άγ―γ€γƒ‡γƒΌγ‚ΏγŒζΊ–ε‚™γ§γγ‚‹γΎγ§ε€γ„ι…ε»Άγ•γ‚ŒγŸε€€γ‚’θ¦‹ηΆšγ‘γΎγ™γ€‚

ι…ε»Άγ•γ‚ŒγŸγ€Œγƒγƒƒγ‚―γ‚°γƒ©γ‚¦γƒ³γƒ‰γ€γƒ¬γƒ³γƒ€γƒΌγ―δΈ­ζ–­ε―θƒ½γ§γ™γ€‚δΎ‹γˆγ°γ€ε†εΊ¦ε…₯εŠ›ζ¬„γ«γ‚Ώγ‚€γƒ—γ‚’θ‘Œγ†γ¨γ€React γ―γγ‚Œγ‚’ζ”Ύζ£„γ—γ€ζ–°γ—γ„ε€€γ§γ‚„γ‚Šη›΄γ—γΎγ™γ€‚React γ―εΈΈγ«ζœ€εΎŒγ«ζδΎ›γ•γ‚ŒγŸε€€γ‚’δ½Ώη”¨γ—γΎγ™γ€‚

ε„γ‚­γƒΌγ‚Ήγƒˆγƒ­γƒΌγ‚―γ”γ¨γ«γƒγƒƒγƒˆγƒ―γƒΌγ‚―γƒͺγ‚―γ‚¨γ‚Ήγƒˆγ―η™Ίη”Ÿγ—γ¦γ„γ‚‹γ“γ¨γ«ζ³¨ζ„γ—γ¦γγ γ•γ„γ€‚γ“γ“γ§οΌˆζΊ–ε‚™γŒγ§γγ‚‹γΎγ§οΌ‰ι…ε»Άγ•γ›γ¦γ„γ‚‹οΏ½?は硐果�?θ‘¨η€Ίγ§γ‚γ‚Šγ€γƒγƒƒγƒˆγƒ―γƒΌγ‚―γƒͺγ‚―γ‚¨γ‚Ήγƒˆθ‡ͺδ½“γ§γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚γƒ¦γƒΌγ‚ΆγŒε…₯εŠ›γ‚’ηΆšγ‘γŸε ΄εˆγ§γ‚‚γ€ε„γ‚­γƒΌγ‚Ήγƒˆγƒ­γƒΌγ‚―οΏ½?レスポンスはキャッシγƒ₯γ•γ‚Œγ¦γ„γ‚‹γŸγ‚γ€Backspace γ‚’ζŠΌγ™γ¨ε³εΊ§γ«εεΏœγ—γ€ε†εΊ¦οΏ½?フェッチは衷きません。


γ‚³γƒ³γƒ†γƒ³γƒ„γŒε€γ„γ“γ¨γ‚’γ‚€γƒ³γ‚Έγ‚±γƒΌγ‚Ώγ§θ‘¨η€Ίγ™γ‚‹

上記�?δΎ‹γ§γ―γ€ζœ€ζ–°οΏ½?クエγƒͺοΏ½?硐果γƒͺγ‚ΉγƒˆγŒγΎγ γƒ­γƒΌγƒ‰δΈ­γ§γ‚γ‚‹γ“γ¨γ‚’η€Ίγ™γ‚€γƒ³γ‚Έγ‚±γƒΌγ‚ΏγŒγ‚γ‚ŠγΎγ›γ‚“γ€‚ζ–°γ—γ„η΅ζžœγŒγƒ­γƒΌγƒ‰γ•γ‚Œγ‚‹οΏ½?γ«ζ™‚ι–“γŒγ‹γ‹γ‚‹γ¨γ€γƒ¦γƒΌγ‚ΆοΏ½?ζ··δΉ±γ‚’ζ‹›γε―θƒ½ζ€§γŒγ‚γ‚ŠγΎγ™γ€‚η΅ζžœγƒͺγ‚ΉγƒˆγŒζœ€ζ–°οΏ½?クエγƒͺと一致していγͺγ„γ“γ¨γ‚’γƒ¦γƒΌγ‚Άγ«ζ˜Žη’Ίγ«δΌγˆγ‚‹γŸγ‚γ«γ€ε€γ„η΅ζžœγƒͺγ‚ΉγƒˆγŒθ‘¨η€Ίγ•γ‚Œγ¦γ„γ‚‹γ¨γγ«θ¦–θ¦šηš„γͺγ‚€γƒ³γ‚Έγ‚±γƒΌγ‚Ώγ‚’θΏ½εŠ γ™γ‚‹γ“γ¨γŒγ§γγΎγ™οΌš

<div style={{
opacity: query !== deferredQuery ? 0.5 : 1,
}}>
<SearchResults query={deferredQuery} />
</div>

γ“γ‚Œγ«γ‚ˆγ‚Šγ€ε…₯εŠ›γ‚’ι–‹ε§‹γ™γ‚‹γ¨η›΄γ‘γ«γ€ε€γ„η΅ζžœγƒͺγ‚ΉγƒˆγŒγ‚γšγ‹γ«ζš—γγͺγ‚Šγ€ζ–°γ—γ„η΅ζžœγƒͺγ‚ΉγƒˆγŒγƒ­γƒΌγƒ‰γ•γ‚Œγ‚‹γΎγ§γοΏ½?ηŠΆζ…‹γŒηΆšγγΎγ™γ€‚δ»₯δΈ‹οΏ½?δΎ‹οΏ½?γ‚ˆγ†γ«γ€ζš—γγͺγ‚‹οΏ½?を遅廢させる CSS γƒˆγƒ©γƒ³γ‚Έγ‚·γƒ§γƒ³γ‚’θΏ½εŠ γ™γ‚‹γ“γ¨γ§γ€εΎγ€…γ«ε€‰εŒ–γ™γ‚‹γ‚ˆγ†γ«ζ„Ÿγ˜γ•γ›γ‚‹γ“γ¨γ‚‚γ§γγΎγ™γ€‚

import { Suspense, useState, useDeferredValue } from 'react';
import SearchResults from './SearchResults.js';

export default function App() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);
  const isStale = query !== deferredQuery;
  return (
    <>
      <label>
        Search albums:
        <input value={query} onChange={e => setQuery(e.target.value)} />
      </label>
      <Suspense fallback={<h2>Loading...</h2>}>
        <div style={{
          opacity: isStale ? 0.5 : 1,
          transition: isStale ? 'opacity 0.2s 0.2s linear' : 'opacity 0s 0s linear'
        }}>
          <SearchResults query={deferredQuery} />
        </div>
      </Suspense>
    </>
  );
}


UI οΏ½?δΈ€ιƒ¨εˆ†οΏ½?再レンダーを遅廢させる

useDeferredValue γ‚’γƒ‘γƒ•γ‚©γƒΌγƒžγƒ³γ‚Ήζœ€ι©εŒ–γ¨γ—γ¦ι©η”¨γ™γ‚‹γ“γ¨γ‚‚γ§γγΎγ™γ€‚γ“γ‚Œγ―γ€UI οΏ½?一部�?ε†γƒ¬γƒ³γƒ€γƒΌγ«ζ™‚ι–“γŒγ‹γ‹γ‚Šγ€γγ‚Œγ‚’ζœ€ι©εŒ–γ™γ‚‹η°‘ε˜γͺζ–Ήζ³•γŒγͺγ„γŒγ€γγ‚Œγ«γ‚ˆγ£γ¦ UI οΏ½?δ»–οΏ½?ιƒ¨εˆ†γŒγƒ–γƒ­γƒƒγ‚―γ•γ‚Œγ‚‹οΏ½?γ‚’ι˜²γŽγŸγ„γ€γ¨γ„γ†ε ΄εˆγ«ζœ‰η”¨γ§γ™γ€‚

γƒ†γ‚­γ‚Ήγƒˆγƒ•γ‚£γƒΌγƒ«γƒ‰γ¨γ€ε„γ‚­γƒΌγ‚Ήγƒˆγƒ­γƒΌγ‚―γ”γ¨γ«ε†γƒ¬γƒ³γƒ€γƒΌγ™γ‚‹γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΌˆγƒγƒ£γƒΌγƒˆγ‚„ι•·γ„γƒͺγ‚Ήγƒˆγͺγ©οΌ‰γŒγ‚γ‚‹γ¨ζƒ³εƒγ—γ¦γΏγ¦γγ γ•γ„οΌš

function App() {
const [text, setText] = useState('');
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<SlowList text={text} />
</>
);
}

まずは SlowList γ‚’ζœ€ι©εŒ–γ—γ¦γ€props γŒεŒγ˜ε ΄εˆγ―ε†γƒ¬γƒ³γƒ€γƒΌγ‚’γ‚Ήγ‚­γƒƒγƒ—γ™γ‚‹γ‚ˆγ†γ«γ—γΎγ™γ€‚γ“γ‚Œγ‚’θ‘Œγ†γ«γ―γ€memo でラップします。

const SlowList = memo(function SlowList({ text }) {
// ...
});

γ—γ‹γ—γ€γ“γ‚ŒγŒζœ‰η”¨γͺοΏ½?は SlowList οΏ½? props γŒε‰ε›žοΏ½?γƒ¬γƒ³γƒ€γƒΌζ™‚γ¨εŒδΈ€γ§γ‚γ‚‹ε ΄εˆοΏ½?γΏγ§γ™γ€‚ηΎεœ¨η›΄ι’γ—γ¦γ„γ‚‹ε•ι‘Œγ―γ€props γŒη•°γͺγ£γ¦γŠγ‚ŠηΎγ«εˆ₯οΏ½?θ¦‹γŸοΏ½?οΏ½?η΅ζžœγ‚’θ‘¨η€Ίγ—γͺいといけγͺγ„ε ΄εˆγ«ι…γ„γ€γ¨γ„γ†γ“γ¨γ§γ™γ€‚

ε…·δ½“ηš„γ«γ―γ€δΈ»γͺγƒ‘γƒ•γ‚©γƒΌγƒžγƒ³γ‚ΉοΏ½?ε•ι‘Œγ―γ€ε…₯εŠ›γƒ•γ‚£γƒΌγƒ«γƒ‰γ«δ½•γ‹γ‚’ε…₯εŠ›γ™γ‚‹γŸγ³γ«γ€SlowList γŒζ–°γ—γ„ props γ‚’ε—γ‘ε–γ‚Šγ€γοΏ½?ツγƒͺγƒΌε…¨δ½“γ‚’ε†γƒ¬γƒ³γƒ€γƒΌγ™γ‚‹γŸγ‚γ€ε…₯εŠ›γŒγ‚‚γŸγ€γζ„Ÿγ˜γ«γͺるということです。こ�?γ‚ˆγ†γͺケースでは、useDeferredValue を使うことで、ε…₯εŠ›γƒ•γ‚£γƒΌγƒ«γƒ‰οΏ½?ζ›΄ζ–°οΌˆι€Ÿγγͺγ‘γ‚Œγ°γͺらγͺγ„οΌ‰γ‚’η΅ζžœγƒͺγ‚ΉγƒˆοΏ½?ζ›΄ζ–°οΌˆι…γγ¦γ‚‚θ¨±γ•γ‚Œγ‚‹οΌ‰γ‚ˆγ‚Šγ‚‚ε„ͺε…ˆγ™γ‚‹γ“γ¨γŒε―θƒ½γ§γ™γ€‚

function App() {
const [text, setText] = useState('');
const deferredText = useDeferredValue(text);
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<SlowList text={deferredText} />
</>
);
}

γ“γ‚Œγ«γ‚ˆγ£γ¦ SlowList οΏ½?再レンダーθ‡ͺδ½“γ‚’ι«˜ι€ŸεŒ–γ—γ¦γ„γ‚‹γ‚γ‘γ§γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚γ—γ‹γ—γ€React に対して、γƒͺγ‚ΉγƒˆοΏ½?再レンダーはε„ͺε…ˆεΊ¦γ‚’δΈ‹γ’γ¦γ‚‚θ‰―γ„γ¨δΌγˆγ‚‹γ“γ¨γ§γ€γ‚­γƒΌγ‚Ήγƒˆγƒ­γƒΌγ‚―γ‚’γƒ–γƒ­γƒƒγ‚―γ—γͺγ„γ‚ˆγ†γ«γ—γΎγ™γ€‚γƒͺγ‚Ήγƒˆγ―ε…₯εŠ›γƒ•γ‚£γƒΌγƒ«γƒ‰οΏ½?γ€ŒεΎŒγ‚’θΏ½γ†γ€ε½’γ«γͺγ‚Šγ€γοΏ½?εΎŒγ€ŒθΏ½γ„γ€γγΎγ™γ€γ€‚ε…ƒγ¨εŒζ§˜γ«γ€React はできるだけ早くγƒͺγ‚Ήγƒˆγ‚’ζ›΄ζ–°γ—γ‚ˆγ†γ¨γ—γΎγ™γŒγ€γƒ¦γƒΌγ‚ΆοΏ½?ε…₯εŠ›γ‚’γƒ–γƒ­γƒƒγ‚―γ™γ‚‹γ“γ¨γ―γͺくγͺγ‚ŠγΎγ™γ€‚

useDeferredValue γ¨ζœ€ι©εŒ–γ•γ‚Œγ¦γ„γͺい再レンダー�?違い

δΎ‹ 1/2:
ι…ε»Άγ•γ‚ŒγŸγƒͺγ‚ΉγƒˆοΏ½?再レンダー

こ�?例では、useDeferredValue がε…₯εŠ›γ‚’γƒ¬γ‚Ήγƒγƒ³γ‚·γƒ–γ«δΏγ€ζ–Ήζ³•γ‚’η’Ίθͺγ§γγ‚‹γ‚ˆγ†γ€SlowList γ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆοΏ½?ε„γ‚’γ‚€γƒ†γƒ γŒδΊΊη‚Ίηš„γ«ι…ε»Άγ•γ›γ‚‰γ‚Œγ¦γ„γΎγ™γ€‚ε…₯εŠ›γƒ•γ‚£γƒΌγƒ«γƒ‰γ«ε…₯εŠ›γ—γ¦γΏγ¦γ€γ‚Ήγƒ γƒΌγ‚Ήγ«ε…₯εŠ›γ§γγ‚‹δΈ€ζ–Ήγ§γ€γƒͺγ‚ΉγƒˆγŒγγ‚Œγ‚’γ€ŒθΏ½γ„γ‹γ‘γ‚‹γ€ζ§˜ε­γ‚’η’Ίθͺγ—てください。

import { useState, useDeferredValue } from 'react';
import SlowList from './SlowList.js';

export default function App() {
  const [text, setText] = useState('');
  const deferredText = useDeferredValue(text);
  return (
    <>
      <input value={text} onChange={e => setText(e.target.value)} />
      <SlowList text={deferredText} />
    </>
  );
}

落とし穴

こ�?ζœ€ι©εŒ–γŒε‹•δ½œγ™γ‚‹γ«γ― SlowList が memo γ§γƒ©γƒƒγƒ—γ•γ‚Œγ¦γ„γ‚‹γ“γ¨γŒεΏ…θ¦γ§γ™γ€‚γ“γ‚Œγ―γ€text γŒε€‰ζ›΄γ•γ‚Œγ‚‹γŸγ³γ«γ€React がθ¦ͺγ‚³γƒ³γƒγƒΌγƒγƒ³γƒˆε΄γ‚’η΄ ζ—©γε†γƒ¬γƒ³γƒ€γƒΌγ§γγ‚‹γ‚ˆγ†γ«γ™γ‚‹εΏ…θ¦γŒγ‚γ‚‹γ‹γ‚‰γ§γ™γ€‚γοΏ½?再レンダー中には、deferredText はまだ前�?倀にγͺγ£γ¦γŠγ‚Šγ€SlowList は(props γŒε€‰ζ›΄γ•γ‚Œγ¦γ„γͺい�?で)再レンダーをスキップできます。memo がγͺγ‘γ‚Œγ°γ€SlowList γ―εΈΈγ«ε†γƒ¬γƒ³γƒ€γƒΌγ•γ‚Œγ¦γ—γΎγ„γ€ζœ€ι©εŒ–οΏ½?ζ„ε‘³γŒε€±γ‚γ‚Œγ¦γ—γΎγ„γΎγ™γ€‚

さらに深くηŸ₯γ‚‹

ε€€οΏ½?ι…ε»Άγ¨γƒ‡γƒγ‚¦γƒ³γ‚Ήγ‚„γ‚Ήγƒ­γƒƒγƒˆγƒͺングと�?違い

こ�?γ‚ˆγ†γͺγ‚·γƒŠγƒͺγ‚ͺγ«γŠγ„γ¦δ½Ώγ£γŸγ“γ¨γŒγ‚γ‚‹γ‹γ‚‚γ—γ‚Œγͺγ„γ€γ‚ˆγγ‚γ‚‹ζœ€ι©εŒ–ζ‰‹ζ³•γŒ 2 γ€γ‚γ‚ŠγΎγ™γ€‚

  • デバウンス (debounce) γ―γ€γƒ¦γƒΌγ‚ΆγŒε…₯εŠ›γ‚’οΌˆδΎ‹γˆγ° 1 η§’ι–“οΌ‰εœζ­’γ™γ‚‹γΎγ§γƒͺγ‚ΉγƒˆοΏ½?更新を待぀という意味です。
  • γ‚Ήγƒ­γƒƒγƒˆγƒͺング (throttling) は、一�?οΏ½οΏ½?ι–“ιš”οΌˆδΎ‹γˆγ°ζœ€ε€§γ§ 1 秒に 1 ε›žοΌ‰γ§γƒͺγ‚Ήγƒˆγ‚’ζ›΄ζ–°γ™γ‚‹γ¨γ„γ†ζ„ε‘³γ§γ™γ€‚

γ“γ‚Œγ‚‰οΏ½?手法は一部�?γ‚±γƒΌγ‚Ήγ§ε½Ήη«‹γ‘γΎγ™γŒγ€useDeferredValue は React θ‡ͺδ½“γ¨ζ·±γη΅±εˆγ•γ‚Œγ¦γŠγ‚Šγ€γƒ¦γƒΌγ‚ΆοΏ½?γƒ‡γƒγ‚€γ‚Ήγ«ι©εΏœγ™γ‚‹γŸγ‚γ€γƒ¬γƒ³γƒ€γƒΌοΏ½?ζœ€ι©εŒ–γ«γ‚ˆγ‚Šι©γ—γ¦γ„γΎγ™γ€‚

γƒ‡γƒγ‚¦γƒ³γ‚Ήγ‚„γ‚Ήγƒ­γƒƒγƒˆγƒͺングとは異γͺγ‚Šγ€ι…ε»Άγ•γ‚Œγ‚‹ζ™‚ι–“γ‚’ε›ΊοΏ½?οΏ½γ§ιΈγΆεΏ…θ¦γ―γ‚γ‚ŠγΎγ›γ‚“γ€‚γƒ¦γƒΌγ‚ΆοΏ½?γƒ‡γƒγ‚€γ‚ΉγŒι€Ÿγ„ε ΄εˆοΌˆδΎ‹γˆγ°γƒ‘γƒ―γƒ•γƒ«γͺγƒ©γƒƒγƒ—γƒˆγƒƒγƒ—οΌ‰γ€ι…ε»Άγ•γ‚ŒγŸε†γƒ¬γƒ³γƒ€γƒΌγ―γ»γΌε³εΊ§γ«θ‘Œγ‚γ‚Œγ‚‹γŸγ‚γ€ζ°—γ₯γ‹γ‚ŒγΎγ›γ‚“γ€‚γƒ¦γƒΌγ‚ΆοΏ½?γƒ‡γƒγ‚€γ‚ΉγŒι…γ„ε ΄εˆγ€γƒͺγ‚Ήγƒˆγ―γƒ‡γƒγ‚€γ‚ΉοΏ½?ι…γ•γ«ζ―”δΎ‹γ™γ‚‹γ‚ˆγ†γ«ε…₯εŠ›γ‹γ‚‰γ€Œι…γ‚Œγ€γ¦γ„γγΎγ™γ€‚

γΎγŸγ€γƒ‡γƒγ‚¦γƒ³γ‚Ήγ‚„γ‚Ήγƒ­γƒƒγƒˆγƒͺングとは異γͺγ‚Šγ€useDeferredValue γ«γ‚ˆγ‚‹ι…ε»Άγ•γ‚ŒγŸε†γƒ¬γƒ³γƒ€γƒΌγ―γƒ‡γƒ•γ‚©γƒ«γƒˆγ§δΈ­ζ–­ε―θƒ½γ§γ™γ€‚γ“γ‚Œγ―γ€React が倧きγͺγƒͺγ‚Ήγƒˆγ‚’ε†γƒ¬γƒ³γƒ€γƒΌγ—γ¦γ„γ‚‹ι€”δΈ­γ§γ€γƒ¦γƒΌγ‚ΆγŒεˆ₯οΏ½?γ‚­γƒΌγ‚Ήγƒˆγƒ­γƒΌγ‚―γ‚’θ‘Œγ†γ¨γ€React はそ�?ε†γƒ¬γƒ³γƒ€γƒΌγ‚’ζ”Ύζ£„γ—γ€γ‚­γƒΌγ‚Ήγƒˆγƒ­γƒΌγ‚―γ‚’ε‡¦η†γ—γ€ε†γ³γƒγƒƒγ‚―γ‚°γƒ©γ‚¦γƒ³γƒ‰γ§γƒ¬γƒ³γƒ€γƒΌγ‚’γ‚„γ‚Šη›΄γ›γ‚‹γ¨γ„γ†ζ„ε‘³γ§γ™γ€‚ε―Ύη…§ηš„γ«γ€γƒ‡γƒγ‚¦γƒ³γ‚Ήγ‚„γ‚Ήγƒ­γƒƒγƒˆγƒͺング�?ε‹•δ½œγ―γƒ–γƒ­γƒƒγ‚­γƒ³γ‚°γ§γ‚γ‚‹γŸγ‚γ€γ‚„γ―γ‚ŠδΈεΏ«γͺδ½“ι¨“γ‚’η”ŸγΏε‡Ίγ—γΎγ™γ€‚γγ‚Œγ‚‰γ―γƒ¬γƒ³γƒ€γƒΌγŒγ‚­γƒΌγ‚Ήγƒˆγƒ­γƒΌγ‚―γ‚’γƒ–γƒ­γƒƒγ‚―γ™γ‚‹γ‚Ώγ‚€γƒŸγƒ³γ‚°γ‚’ε˜γ«ι…γ‚‰γ›γ¦γ„γ‚‹γ«ιŽγŽγͺい�?です。

ζœ€ι©εŒ–γ—γ‚ˆγ†γ¨γ—γ¦γ„γ‚‹δ½œζ₯­γŒγƒ¬γƒ³γƒ€γƒΌοΏ½?ζœ€δΈ­γ«θ‘Œγ‚γ‚Œγ‚‹γ‚‚οΏ½?でγͺγ„ε ΄εˆγ€γƒ‡γƒγ‚¦γƒ³γ‚Ήγ¨γ‚Ήγƒ­γƒƒγƒˆγƒͺγƒ³γ‚°γ―δΎη„Άγ¨γ—γ¦ζœ‰η”¨γ§γ™γ€‚δΎ‹γˆγ°γ€γƒγƒƒγƒˆγƒ―γƒΌγ‚―γƒͺγ‚―γ‚¨γ‚ΉγƒˆοΏ½?ε›žζ•°γ‚’ζΈ›γ‚‰γ™γ“γ¨γŒγ§γγΎγ™γ€‚γ“γ‚Œγ‚‰οΏ½?手法を一緒に使用することもできます。