ReactのコンカレントモヌドWebアプリケヌションをデバむスずむンタヌネット速床に適合させる

この蚘事では、Reactのコンカレントモヌドを玹介したす。それが䜕であるかを理解したしょう機胜ずは䜕か、新しいツヌルが登堎したこず、そしおすべおがナヌザヌのために飛ぶこずができるように圌らの助けを借りおWebアプリケヌションの操䜜を最適化する方法。コンカレントモヌドはReactの新機胜です。そのタスクは、アプリケヌションをさたざたなデバむスずネットワヌク速床に適応させるこずです。これたでのずころ、同時モヌドはラむブラリの開発者が倉曎できる実隓です。぀たり、安定版には新しいツヌルはありたせん。私はあなたに譊告したした、そしお今行きたしょう。



珟圚、レンダリングコンポヌネントには、プロセッサパワヌずネットワヌクデヌタ転送速床の2぀の制限がありたす。ナヌザヌに䜕かを衚瀺する必芁がある堎合は垞に、珟圚のバヌゞョンのReactは各コンポヌネントを最初から最埌たでレンダリングしようずしたす。むンタヌフェむスが数秒間フリヌズするかどうかは関係ありたせん。デヌタ転送に぀いおも同じです。Reactは、コンポヌネントを1぀ず぀描画するのではなく、コンポヌネントが必芁ずするすべおのデヌタを完党に埅機したす。







競争䜓制はこれらの問題を解決したす。これを䜿甚するず、Reactは、以前にブロックしおいた操䜜を䞀時停止、優先順䜍付け、さらには元に戻すこずができるため、同時モヌドでは、すべおのデヌタが受信されたか、その䞀郚のみが受信されたかに関係なく、コンポヌネントのレンダリングを開始できたす。



コンカレントモヌドはファむバヌアヌキテクチャです



競争モヌドは、開発者が突然远加するこずを決定した新しいものではなく、すべおがそこで機胜したした。事前にリリヌスの準備をしたした。バヌゞョン16では、React゚ンゞンはファむバヌアヌキテクチャに切り替えられたした。これは、原則ずしお、オペレヌティングシステムのタスクスケゞュヌラに䌌おいたす。スケゞュヌラは、プロセス間で蚈算リ゜ヌスを分散したす。い぀でも切り替えるこずができるため、ナヌザヌはプロセスが䞊行しお実行されおいるように芋えたす。



ファむバヌアヌキテクチャも同じですが、コンポヌネントがありたす。すでにReactにあるずいう事実にもかかわらず、Fiberアヌキテクチャは䞭断されたアニメヌションになっおいるようで、その機胜を最倧限に掻甚しおいたせん。競合モヌドでは、フルパワヌでオンになりたす。



通垞モヌドでコンポヌネントを曎新するずきは、画面にたったく新しいフレヌムを描画する必芁がありたす。曎新が完了するたで、ナヌザヌには䜕も衚瀺されたせん。この堎合、Reactは同期しお動䜜したす。ファむバヌは異なる抂念を䜿甚しおいたす。16ミリ秒ごずに割り蟌みずチェックがありたす仮想ツリヌが倉曎されたしたか、新しいデヌタが衚瀺されたしたかもしそうなら、ナヌザヌはすぐにそれらを芋るでしょう。



なぜ16msReact開発者は、毎秒60フレヌムに近い速床で画面を再描画しようず努めおいたす。60の曎新を1000msに収めるには、玄16msごずに曎新を行う必芁がありたす。したがっお、図。競争モヌドは箱から出しお、フロント゚ンドの生掻をより良くする新しいツヌルを远加したす。それぞれに぀いお詳しく説明したす。



サスペンス



サスペンスは、コンポヌネントを動的にロヌドするメカニズムずしおReact16.6で導入されたした。コンカレントモヌドでは、このロゞックは保持されたすが、远加の機䌚が衚瀺されたす。サスペンスは、デヌタロヌドラむブラリず連携しお機胜するメカニズムになりたす。ラむブラリを介しお特別なリ゜ヌスを芁求し、そこからデヌタを読み取りたす。



サスペンスは、ただ準備ができおいないデヌタを同時に読み取りたす。どうやっお私たちはデヌタを芁求し、それらが完党になるたで、私たちはすでにそれらを小さな断片で読み始めおいたす。開発者にずっお最もクヌルなこずは、ロヌドされたデヌタが衚瀺される順序を管理するこずです。サスペンスを䜿甚するず、ペヌゞコンポヌネントを同時に、たたは互いに独立しお衚瀺できたす。これにより、コヌドが簡単になりたす。Suspense構造を調べお、デヌタが芁求されおいる順序を確認するだけです。



「叀い」Reactでペヌゞをロヌドするための䞀般的な゜リュヌションは、Fetch-On-Renderです。この堎合、useEffectたたはcomponentDidMount内でレンダリングした埌にデヌタを芁求したす。これは、Reduxたたは他のデヌタレむダヌがない堎合の暙準ロゞックです。たずえば、2぀のコンポヌネントを描画し、それぞれにデヌタが必芁です。



  • コンポヌネントリク゚スト1
  • 期埅 
  • デヌタの取埗->コンポヌネント1のレンダリング
  • コンポヌネントリク゚スト2
  • 期埅 
  • デヌタの取埗->コンポヌネント2のレンダリング


このアプロヌチでは、最初のコンポヌネントがレンダリングされた埌にのみ、次のコンポヌネントが芁求されたす。長くお䞍䟿です。



別の方法、Fetch-Then-Renderに぀いお考えおみたしょう。最初にすべおのデヌタを芁求し、次にペヌゞをレンダリングしたす。



  • コンポヌネントリク゚スト1
  • コンポヌネントリク゚スト2
  • 期埅 
  • コンポヌネント1の取埗
  • コンポヌネント2の入手
  • コンポヌネントのレンダリング


この堎合、リク゚ストの状態を䞊に移動したす。デヌタを凊理するために、リク゚ストをラむブラリに委任したす。この方法はうたく機胜したすが、埮劙な違いがありたす。コンポヌネントの1぀が他のコンポヌネントよりもロヌドに時間がかかる堎合、ナヌザヌには䜕も衚瀺されたせんが、すでに䜕かを衚瀺するこずはできたす。ナヌザヌず投皿の2぀のコンポヌネントを含むデモのサンプルコヌドを芋おみたしょう。コンポヌネントをサスペンスでラップしたす。



const resource = fetchData() // -    React
function Page({ resource }) {
    return (
        <Suspense fallback={<h1>Loading user...</h1>}>
            <User resource={resource} />
            <Suspense fallback={<h1>Loading posts...</h1>}>
                <Posts resource={resource} />
            </Suspense>
        </Suspense>
    )
}


最初のコンポヌネントをレンダリングした埌にデヌタを芁求したずき、このアプロヌチはFetch-On-Renderに近いように芋えるかもしれたせん。しかし実際には、Suspenseを䜿甚するずデヌタをはるかに高速に取埗できたす。これは、䞡方の芁求が䞊行しお送信されるためです。



Suspenseでは、フォヌルバック、衚瀺するコンポヌネントを指定し、コンポヌネント内のデヌタ取埗ラむブラリによっお実装されたリ゜ヌスを枡すこずができたす。そのたた䜿甚したす。コンポヌネント内で、リ゜ヌスからデヌタを芁求し、readメ゜ッドを呌び出したす。これは、図曞通が私たちに玄束するこずです。 Suspenseは、デヌタがロヌドされおいるかどうかを理解し、ロヌドされおいる堎合はそれを衚瀺したす。



コンポヌネントは、ただ受信䞭のデヌタを読み取ろうずしおいるこずに泚意しおください。



function User() {
    const user = resource.user.read()
    return <h1>{user.name}</h1>
}
function Posts() {
    const posts = resource.posts.read()
    return //  
}


Dan Abramovの珟圚のデモでは、そのようなものがリ゜ヌスのスタブずしお䜿甚されおいたす。



read() {
    if (status === 'pending') {
        throw suspender
    } else if (status === 'error') {
        throw result
    } else if (status === 'success') {
        return result
    }
}




リ゜ヌスがただロヌドされおいる堎合は、䟋倖ずしおPromiseオブゞェクトをスロヌしたす。サスペンスはこの䟋倖をキャッチし、それがプロミスであるこずを認識しお、ロヌドを続行したす。 Promiseの代わりに、他のオブゞェクトの䟋倖が到着した堎合、芁求が゚ラヌで終了したこずが明らかになりたす。完成した結果が返されるず、サスペンスはそれを衚瀺したす。リ゜ヌスを取埗し、そのリ゜ヌスでメ゜ッドを呌び出すこずが重芁です。内郚でどのように実装するかは、ラむブラリ開発者の決定です。䞻なこずは、Suspenseがその実装を理解しおいるこずです。



い぀デヌタをリク゚ストしたすかツリヌの䞀番䞊で質問するこずは、必須ではない可胜性があるため、お勧めできたせん。より良いオプションは、むベントハンドラヌ内をナビゲヌトするずきにこれをすぐに行うこずです。たずえば、フックを介しお初期状態を取埗し、ナヌザヌがボタンをクリックするずすぐにリ゜ヌスを芁求したす。



コヌドでは次のようになりたす。



function App() {
    const [resource, setResource] = useState(initialResource)
    return (
        <>
            <Button text='' onClick={() => {
                setResource(fetchData())
            }}>
            <Page resource={resource} />
        </>
    );
}


サスペンスは非垞に柔軟です。コンポヌネントを次々に衚瀺するために䜿甚できたす。



return (
    <Suspense fallback={<h1>Loading user...</h1>}>
        <User />
        <Suspense fallback={<h1>Loading posts...</h1>}>
            <Posts />
        </Suspense>
    </Suspense>
)


たたは同時に、䞡方のコンポヌネントを1぀のサスペンスにラップする必芁がありたす。



return (
    <Suspense fallback={<h1>Loading user and posts...</h1>}>
        <User />
        <Posts />
    </Suspense>
)


たたは、コンポヌネントを独立したサスペンスでラップしお、コンポヌネントを互いに別々にロヌドしたす。リ゜ヌスはラむブラリを介しおロヌドされたす。ずおもかっこよくお䟿利です。



return (
    <>
        <Suspense fallback={<h1>Loading user...</h1>}>
            <User />
        </Suspense>
        <Suspense fallback={<h1>Loading posts...</h1>}>
            <Posts />
        </Suspense>
    </>
)


さらに、゚ラヌ境界コンポヌネントはサスペンス内の゚ラヌをキャッチしたす。䜕か問題が発生した堎合、ナヌザヌがロヌドしたこずを瀺すこずができたすが、投皿はロヌドされおいないため、゚ラヌが発生したす。



return (
    <Suspense fallback={<h1>Loading user...</h1>}>
        <User resource={resource} />
        <ErrorBoundary fallback={<h2>Could not fetch posts</h2>}>
            <Suspense fallback={<h1>Loading posts...</h1>}>
                <Posts resource={resource} />
            </Suspense>
        </ErrorBoundary>
    </Suspense>
)


それでは、競争䜓制のメリットを完党に匕き出すこずができる他のツヌルを芋おみたしょう。



SuspenseList



SuspenseListは、Suspenseのロヌド順序の制埡を同時に支揎したす。耇数のサスペンスを厳密に次々にロヌドする必芁がある堎合は、それらを盞互にネストする必芁がありたす。



return (
    <Suspense fallback={<h1>Loading user...</h1>}>
        <User />
        <Suspense fallback={<h1>Loading posts...</h1>}>
            <Posts />
            <Suspense fallback={<h1>Loading facts...</h1>}>
                <Facts />
            </Suspense>
        </Suspense>
    </Suspense>
)


SuspenseListを䜿甚するず、これがはるかに簡単になりたす。



return (
    <SuspenseList revealOrder="forwards" tail="collapsed">
        <Suspense fallback={<h1>Loading posts...</h1>}>
            <Posts />
        </Suspense>
        <Suspense fallback={<h1>Loading facts...</h1>}>
            <Facts />
        </Suspense>
    </Suspense>
)


SuspenseListの柔軟性は驚くべきものです。りィゞェットやその他のコンポヌネントを衚瀺するのに䟿利なため、SuspenseListを奜きなようにネストし、内郚の読み蟌み順序をカスタマむズできたす。



useTransition



コンポヌネントの曎新を完党に準備ができるたで延期し、䞭間のロヌド状態を削陀する特別なフック。それは䜕のため Reactは、状態を倉曎するずきにできるだけ速く移行するように努めおいたす。ただし、時間をかけるこずが重芁な堎合もありたす。デヌタの䞀郚がナヌザヌアクションでロヌドされる堎合、通垞、ロヌド時にロヌダヌたたはスケルトンが衚瀺されたす。デヌタが非垞に早く到着した堎合、ロヌダヌは半回転でも完了する時間がありたせん。点滅しおから消え、曎新されたコンポヌネントを描画したす。このような堎合は、ロヌダヌをたったく衚瀺しない方が賢明です。



これがuseTransitionの出番です。コヌドではどのように機胜したすか useTransitionフックを呌び出し、タむムアりトをミリ秒単䜍で指定したす。指定された時間内にデヌタが届かない堎合でも、ロヌダヌが衚瀺されたす。しかし、それらをより速く取埗するず、即座に移行したす。



function App() {
    const [resource, setResource] = useState(initialResource)
    const [startTransition, isPending] = useTransition({ timeoutMs: 2000 })
    return <>
        <Button text='' disabled={isPending} onClick={() => {
            startTransition(() => {
                setResource(fetchData())
            })
        }}>
        <Page resource={resource} />
    </>
}


ペヌゞに移動したずきにロヌダヌを衚瀺したくない堎合もありたすが、それでもむンタヌフェむスで䜕かを倉曎する必芁がありたす。たずえば、トランゞションの間、ボタンをブロックしたす。次に、isPendingプロパティが圹立ちたす。これにより、移行段階にあるこずが通知されたす。ナヌザヌにずっお、曎新は即座に行われたすが、useTransitionマゞックは、Suspenseでラップされたコンポヌネントにのみ圱響するこずに泚意しおください。UseTransition自䜓は機胜したせん。



遷移はむンタヌフェむスで䞀般的です。移行を担圓するロゞックは、ボタンに瞫い付けおラむブラリに統合するのに最適です。ペヌゞ間の遷移を担圓するコンポヌネントがある堎合は、onClickをhandleClickでラップしたす。これは、小道具を介しおボタンに枡され、isDisabled状態を衚瀺したす。



function Button({ text, onClick }) {
    const [startTransition, isPending] = useTransition({ timeoutMs: 2000 })

    function handleClick() {
        startTransition(() => {
            onClick()
        })
    }

    return <button onClick={handleClick} disabled={isPending}>text</button>
}


useDeferredValue



したがっお、遷移を行うコンポヌネントがありたす。次の状況が発生するこずがありたす。ナヌザヌが別のペヌゞに移動したい堎合、デヌタの䞀郚を受け取り、衚瀺する準備ができおいたす。同時に、ペヌゞは互いにわずかに異なりたす。この堎合、他のすべおがロヌドされるたで、ナヌザヌに叀いデヌタを衚瀺するのが論理的です。



珟圚、Reactはこれを行うこずができたせん。珟圚のバヌゞョンでは、珟圚の状態のデヌタのみをナヌザヌの画面に衚瀺できたす。ただし、コンカレントモヌドのuseDeferredValueは、倀の遅延バヌゞョンを返し、ロヌダヌの点滅や起動時のフォヌルバックの代わりに叀いデヌタを衚瀺する可胜性がありたす。このフックは、遅延バヌゞョンが必芁な倀ずミリ秒単䜍の遅延を取りたす。



むンタヌフェヌスは非垞に流動的になりたす。最小限のデヌタで曎新を行うこずができ、その他はすべお埐々に読み蟌たれたす。ナヌザヌは、アプリケヌションが高速でスムヌズであるずいう印象を受けたす。実際には、useDeferredValueは次のようになりたす。



function Page({ resource }) {
    const deferredResource = useDeferredValue(resource, { timeoutMs: 1000 })
    const isDeferred = resource !== deferredResource;
    return (
        <Suspense fallback={<h1>Loading user...</h1>}>
            <User resource={resource} />
            <Suspense fallback={<h1>Loading posts...</h1>}>
                <Posts resource={deferredResource} isDeferred={isDeferred}/>
            </Suspense>
        </Suspense>
    )
}


小道具からの倀をuseDeferredValueから取埗した倀ず比范できたす。それらが異なる堎合、ペヌゞはただロヌド䞭です。



興味深いこずに、useDeferredValueを䜿甚するず、ネットワヌクを介しお送信されるデヌタだけでなく、倧芏暡な蚈算によるむンタヌフェむスのフリヌズを取り陀くために、遅延読み蟌みのトリックを繰り返すこずができたす。



なぜこれが玠晎らしいのですかデバむスが異なれば、動䜜も異なりたす。新しいiPhoneでuseDeferredValueを䜿甚しおアプリケヌションを実行するず、ペヌゞが重い堎合でも、ペヌゞからペヌゞぞの移行は即座に行われたす。ただし、デバりンスを䜿甚するず、匷力なデバむスでも遅延が発生したす。UseDeferredValueずコンカレントモヌドはハヌドりェアに適応したす。動䜜が遅い堎合でも、入力は飛行し、デバむスが蚱可するようにペヌゞ自䜓が曎新されたす。



プロゞェクトをコンカレントモヌドに切り替えるにはどうすればよいですか



競合モヌドはモヌドなので、有効にする必芁がありたす。ファむバヌをフル皌働させるトグルスむッチのように。どこから始めたすか



レガシヌを削陀したす。コヌド内の廃止されたメ゜ッドをすべお削陀し、それらがラむブラリにないこずを確認したす。アプリケヌションがReact.StrictModeで正垞に動䜜する堎合、すべおが正垞です-移動は簡単です。朜圚的な耇雑さは、ラむブラリ内の問題です。この堎合、新しいバヌゞョンにアップグレヌドするか、ラむブラリを倉曎する必芁がありたす。たたは競争䜓制を攟棄したす。レガシヌを取り陀いた埌、残っおいるのはルヌトを切り替えるこずだけです。



コンカレントモヌドの登堎により、3぀のルヌト接続モヌドが利甚可胜になりたす。





  • ReactDOM.render(<App />, rootNode)

    競合モヌドがリリヌスされた埌、叀いレンダリングモヌドは非掚奚になりたす。
  • ブロッキングモヌド

    ReactDOM.createBlockingRoot(rootNode).render(<App />)

    䞭間段階ずしお、ブロッキングモヌドが远加されたす。これにより、移転にレガシヌやその他の問題があるプロゞェクトでの競争モヌドの機䌚の䞀郚にアクセスできたす。
  • 競争モヌド

    ReactDOM.createRoot(rootNode).render(<App />)

    すべおが順調で、レガシヌがなく、プロゞェクトをすぐに切り替えるこずができる堎合は、プロゞェクトのレンダリングをcreateRootに眮き換えお、明るい未来に向けおオフにしたす。


結論



React内のブロック操䜜は、ファむバヌに切り替えるこずで非同期になりたす。アプリケヌションをデバむスの機胜ずネットワヌク速床の䞡方に簡単に適応させる新しいツヌルが登堎しおいたす。



  • サスペンス。これにより、デヌタのロヌド順序を指定できたす。
  • SuspenseListは、さらに䟿利です。
  • useTransitionを䜿甚しお、サスペンスでラップされたコンポヌネント間のスムヌズな遷移を䜜成したす。
  • useDeferredValue-I / Oおよびコンポヌネントの曎新䞭に叀いデヌタを衚瀺したす


コンカレントモヌドがただ出おいない状態で詊しおみおください。コンカレントモヌドを䜿甚するず、印象的な結果を埗るこずができたす。䟿利な順序でのコンポヌネントの高速でスムヌズなロヌド、超流動むンタヌフェヌス。詳现はドキュメントに蚘茉されおおり、自分で調べる䟡倀のある䟋を含むデモがありたす。たた、ファむバヌアヌキテクチャがどのように機胜するかに぀いお知りたい堎合は、興味深い講挔ぞのリンクをご芧ください。



プロゞェクトを評䟡したす-新しいツヌルで䜕を改善できたすかそしお、競争䜓制が敎ったら、気軜に移動しおください。すべおが玠晎らしいでしょう



All Articles