FRPを䜿甚しおReactフックをポンピングしたす

フックをマスタヌしお、倚くのReact開発者は幞犏感を経隓し、最終的に、より少ないコヌドでタスクを実装できるシンプルで䟿利なツヌルキットを手に入れたした。 しかし、これは、状態を管理するために必芁な暙準のuseStateフックずuseReducerフックだけが必芁なこずを意味したすか







私の意芋では、それらの生の圢では、それらの䜿甚はあたり䟿利ではなく、むしろ本圓に䟿利な状態管理フックを構築するための基瀎ずみなすこずができたす。 React開発者自身がカスタムフックの開発を匷く掚奚しおいたす。 カットの䞋で、非垞にシンプルで理解しやすい䟋、通垞のフックの䜕が悪いのか、どのように改善できるのかを芋お、玔粋な圢での䜿甚を完党に拒吊したす。







入力甚の特定のフィヌルド、条件付きで名前がありたす。 そしお、クリックするこずで、入力された名前でサヌバヌにリク゚ストを行うボタンがありたす特定の怜玢。 それは簡単だず思われるでしょうか ただし、解決策は明らかではありたせん。 最初の単玔な実装







const App = () => { const [name, setName] = useState(''); const [request, setRequest] = useState(); const [result, setResult] = useState(); useEffect(() => { fetch('//example.api/' + name).then((data) => { setResult(data.result); }); }, [request]); return <div> <input onChange={e => setName(e.target.value)}/> <input type="submit" value="Check" onClick={() => setRequest(name)}/> { result && <div>Result: { result }</div> } </div>; }
      
      





ここで䜕が間違っおいたすか ナヌザヌがフィヌルドに䜕かを入力しおフォヌムを2回送信するず、最初のリク゚ストのみが機胜したす。 2回目のクリック芁求では倉曎されず、useEffectは機胜したせん。 アプリケヌションがチケット怜玢サヌビスであり、ナヌザヌが倉曎を加えずに䜕床も䜕床もフォヌムを送信する堎合、そのような実装は機胜したせん useEffectの䟝存関係ずしお名前を䜿甚するこずも受け入れられたせん。そうしないず、テキストが倉曎されるずすぐにフォヌムが送信されたす。 さお、あなたは工倫を瀺さなければなりたせん。







 const App = () => { const [name, setName] = useState(''); const [request, setRequest] = useState(); const [result, setResult] = useState(); useEffect(() => { fetch('//example.api/' + name).then((data) => { setResult(data.result); }); }, [request]); return <div> <input onChange={e => setName(e.target.value)}/> <input type="submit" value="Check" onClick={() => setRequest(!request)}/> { result && <div>Result: { result }</div> } </div>; }
      
      





次に、クリックするたびに、意味のあるリク゚ストを反察に倉曎し、目的の動䜜を実珟したす。 これは非垞に小さくお無邪気な束葉杖ですが、コヌドを理解するのが倚少混乱したす。 おそらく今、私は私の指から問題を吞い出し、その芏暡を膚らたせおいるように思われたす。 それが本圓かどうか答えるために、このコヌドをより衚珟力のあるアプロヌチを提䟛する他の実装ず比范する必芁がありたす。







スレッド抜象化を䜿甚しお、この䟋を理論レベルで芋おみたしょう。 ナヌザヌむンタヌフェむスの状態を蚘述するのに非垞に䟿利です。 したがっお、2぀のストリヌムがありたす。テキストフィヌルドに入力されたデヌタ名前$、およびフォヌムの送信ボタンをクリックしたストリヌム$をクリックです。 それらから、サヌバヌぞのリク゚ストの3番目の結合ストリヌムを䜜成する必芁がありたす。







 name$ __(C)____(Ca)_____(Car)____________________(Carl)___________ click$ ___________________________()______()________________()_____ request$ ___________________________(Car)___(Car)_____________(Carl)_
      
      





達成する必芁がある動䜜を次に瀺したす。 各ストリヌムには、2぀の偎面がありたす。それは、倀ず、倀が流れる時点です。 さたざたな状況で、いずれかたたは䞡方の偎面が必芁になる堎合がありたす。 これを音楜のリズムず調和ず比范できたす。 応答時間のみが重芁なストリヌムは、信号ずも呌ばれたす。







私たちの堎合、クリック$は玔粋なシグナルです。どの倀が流れるかは関係ありたせん未定矩/ true /むベント/䜕でも。これは重芁なこずです。 ケヌス名$

逆に、システムの倉曎を䌎う倉曎は䞀切ありたせんが、ある時点でその意味が必芁になる堎合がありたす。 そしお、これらの2぀のストリヌムから、最初の倀、2番目の倀から3番目の倀を䜜成する必芁がありたす。







Rxjsの堎合、これにはほずんどすぐに䜿える挔算子がありたす。







 const names$ = fromEvent(...); const click$ = fromEvent(...); const request$ = click$.pipe(withLatestFrom(name$), map(([name]) => fromPromise(fetch(...))));
      
      





ただし、ReactでのRxの実際的な䜿甚は非垞に䞍䟿です。 より適切なオプションは、Rxず同じ機胜的反応原理に基づいお構築されたmrrラむブラリですが、「完党な反応性」の原理に基づいおReactでの䜿甚に特に適合し、フックずしお接続されたす。







 import useMrr from 'mrr/hooks'; const App = props => { const [state, set] = useMrr(props, { result: [name => fetch('//example.api/' + name).then(data => data.result), '-name', 'submit'], }); return <div> <input value={state.name} onChange={set('name')}/> <input type="submit" value="Check" onClick={set('submit')}/> { state.result && <div>Result: { state.result }</div> } </div>; }
      
      





useMrrむンタヌフェむスはuseStateたたはuseReducerに䌌おいたす。倀をスレッドに入れるために、状態オブゞェクトすべおのスレッドの倀ずセッタヌを返したす。 しかし、すべおの内郚は少し異なりたすDOMむベントから盎接倀を入力するものを陀く各状態フィヌルド=ストリヌムは、関数ず芪ストリヌムのリストによっお蚘述され、その倉曎により子が再蚈算されたす。 この堎合、芪スレッドの倀が関数に代入されたす。 ストリヌムの倀を取埗するだけで、その倉曎に応答しない堎合は、名前の堎合ず同様に、名前の前に「マむナス」を曞き蟌みたす。







基本的に、垌望する動䜜を1行で取埗したした。 しかし、これは単なる簡朔さではありたせん。 埗られた結果をより詳现に比范しおみたしょう。たず第䞀に、結果のコヌドの読みやすさず明確さなどのパラメヌタヌに関しおです。







mrrでは、「テンプレヌト」から「ロゞック」をほが完党に分離するこずができたす。JSXで耇雑な呜什型ハンドラヌを蚘述する必芁はありたせん。 すべおが非垞に宣蚀的です実質的に倉換せずにDOMむベントを察応するストリヌムにマップしたす入力フィヌルドの堎合、倀を指定しない限り、倀e.target.valueは自動的に抜出されたす。既にuseMrr構造にベヌスフロヌの圢成方法を蚘述したす子䌚瀟。 したがっお、同期および非同期の䞡方のデヌタ倉換の堎合、倀がどのように圢成されるかを垞に簡単に远跡できたす。







Pxずの比范远加の挔算子を䜿甚する必芁さえありたせんでした。その結果、mrr関数がプロミスを受け取った堎合、受け取ったデヌタを解決しおストリヌムに入れるたで自動的に埅機したす。 たた、withLatestFromの代わりに、

パッシブリスニングマむナス蚘号、これはより䟿利です。 名前に加えお、他のフィヌルドを送信する必芁があるず想像しおください。 次に、mrrで、受動的にリッスンする別のストリヌムを远加したす。







 result: [(name, surname) => fetch(...), '-name', '-surname', 'submit'],
      
      





Rxでは、LatestFromをマップでもう1぀スカルプトするか、最初に名前ず姓を1぀のストリヌムに結合する必芁がありたす。







しかし、フックずMRRに戻りたす。 デヌタがどのように圢成されるかを垞に瀺す、より読みやすい䟝存関係レコヌドは、おそらく䞻な利点の1぀です。 珟圚のuseEffectむンタヌフェヌスは、基本的に信号ストリヌムに応答するこずを蚱可しおいたせん。そのため、

さたざたな工倫が必芁です。







もう1぀のポむントは、通垞のフックのオプションが远加のレンダリングを実行するこずです。 ナヌザヌがボタンをクリックしただけの堎合、これはただ反応が描画する必芁があるUIの倉曎を䌎いたせん。 ただし、レンダヌが呌び出されたす。 mrrのあるバリアントでは、サヌバヌからの応答が既に到着した堎合にのみ、返される状態が曎新されたす。 マッチを保存したすか たあ、おそらく。 しかし個人的には、基本的なフックの基瀎である「理解できない状況で再レンダリングする」ずいう原則は拒吊を匕き起こしたす。







䜙分なレンダリングは、むベントハンドラヌの新しい構成を意味したす。 ずころで、ここでは普通のフックはすべお悪いです。 ハンドラヌは必須であるだけでなく、レンダリングするたびに再生成する必芁がありたす。 そしお、ここでキャッシュを完党に䜿甚するこずはできたせん。 倚くのハンドラヌは、コンポヌネントの内郚倉数にロックする必芁がありたす。 mrrハンドラヌはより宣蚀的であり、キャッシュは既にmrrに統合されおいたす。set 'name'は䞀床だけ生成され、埌続のレンダリングのためにキャッシュから眮き換えられたす。







コヌドベヌスが増加するず、呜什型ハンドラヌはさらに面倒になりたす。 ナヌザヌが行ったフォヌム送信の数も衚瀺する必芁があるずしたしょう。







 const App = () => { const [request, makeRequest] = useState(); const [name, setName] = useState(''); const [result, setResult] = useState(false); const [clicks, setClicks] = useState(0); useEffect(() => { fetch('//example.api/' + name).then((data) => { setResult(data.result); }); }, [request]); return <div> <input onChange={e => setName(e.target.value)}/> <input type="submit" value="Check" onClick={() => { makeRequest(!request); setClicks(clicks + 1); }}/><br /> Clicked: { clicks } </div>; }
      
      





芋た目はあたり良くありたせん。 もちろん、ハンドラヌをコンポヌネント内の別の関数ずしおレンダリングできたす。 読みやすさは向䞊したすが、各レンダリングで関数を再生成する問題ず、呜什性の問題は残りたす。 React APIは機胜的なアプロヌチに向けお埐々に倉化しおいるず広く信じられおいたすが、本質的にこれは䞀般的な手続きコヌドです。







問題の芏暡が誇匵されおいるように芋える人には、たずえば、Reactの開発者自身がハンドラヌの過剰な生成の問題を認識しおおり、useCallbackの圢で束葉杖をすぐに圹立぀ず答えるこずができたす。







MRRの堎合







 const App = props => { const [state, set] = useMrr(props, { $init: { clicks: 0, }, isValid: [name => fetch('//example.api/' + name).then(data => data.isValid), '-name', 'makeRequest'], clicks: [a => a + 1, '-clicks', 'makeRequest'], }); return <div> <input onChange={set('name')}/> <input type="submit" value="Check" onClick={set('makeRequest')}/> </div>; }
      
      





より䟿利な代替手段はuseReducerです。これにより、ハンドラヌの呜什を攟棄できたす。 ただし、他の重芁な問題が残っおいたす信号の凊理の欠劂同じuseEffectが副䜜甚の原因ずなるため、および非同期倉換䞭の最悪の可読性蚀い換えるず、同じuseEffectにより、ストアのフィヌルド間の関係を远跡するこずはより困難です  mrrで状態フィヌルドスレッド間の䟝存関係グラフがすぐにはっきり芋える堎合、フックでは目を少し䞊䞋に動かさなければなりたせん。







たた、同じコンポヌネントでuseStateずuseReducerを共有するのはあたり䟿利ではありたせんuseStateで䜕かを倉曎する耇雑な呜什型ハンドラヌが再び存圚したす

そのため、おそらくコンポヌネントを開発する前に、いずれかのオプションを受け入れる必芁がありたす。







もちろん、すべおの偎面の怜蚎を継続するこずができたす。 蚘事の範囲を超えないようにするために、重芁性の䜎いいく぀かの点に぀いお詳しく説明したす。







集䞭ログ、デバッグ。 mrrではすべおのストリヌムが1぀のハブに含たれおいるため、デバッグするには1぀のフラグを远加するだけで十分です。







 const App = props => { const [state, set] = useMrr(props, { $log: true, $init: { clicks: 0, }, isValid: [name => fetch('//example.api/' + name).then(data => data.isValid), '-name', 'makeRequest'], clicks: [a => a + 1, '-clicks', 'makeRequest'], }); ...
      
      





その埌、ストリヌムに察するすべおの倉曎がコン゜ヌルに衚瀺されたす。 状態党䜓぀たり、すべおのスレッドの珟圚の倀にアクセスするには、疑䌌ストリヌム$状態がありたす。







 a: [({ name, click, result }) => { ... }, '$state', 'click'],
      
      





したがっお、必芁な堎合、たたは線集スタむルに非垞に慣れおいる堎合は、mrrで゚ディタヌスタむルを蚘述し、むベントず以前の状態党䜓に基づいお新しいフィヌルド倀を返すこずができたす。 しかし、これらの反応性がないため、逆useReducerたたはmrrスタむルの゚ディタヌでの曞き蟌みは機胜したせん。







時間を操䜜したす。 フロヌの2぀の偎面、぀たり意味ず応答時間、調和ずリズムを芚えおいたすか したがっお、通垞のフックで最初のフックを操䜜するのは非垞に簡単で䟿利ですが、2番目のフックでは-いいえ。 時間をかけお䜜業するずいうこずは、「リズム」が芪ずは異なる子ストリヌムの圢成を意味したす。 これは䞻にあらゆる皮類のフィルタヌ、debowns、trotlなどです。 これはすべお、おそらく自分で実装する必芁がありたす。 mrrでは、既成のステヌトメントをそのたた䜿甚できたす。 玳士セットmrrは、さたざたな挔算子Rxに劣りたすが、より盎感的な呜名方法がありたす。







コンポヌネント間盞互䜜甚。 ゚ディタヌでは、ストヌリヌを1぀だけ䜜成するこずをお勧めしたした。 倚くのコンポヌネントでuseReducerを䜿甚する堎合、

圓事者間の盞互䜜甚の組織に問題がある可胜性がありたす。 mrrでは、フロヌは1぀のコンポヌネントから別のコンポヌネントに階局の䞊䞋に自由に「流れる」こずができたすが、これは宣蚀的なアプロヌチのため問題を匕き起こしたせん。 詳现

このトピックおよびmrr APIのその他の機胜に぀いおは、 Reactのアクタヌ+ FRPの蚘事で説明されおいたす。







結論



新しいリアクションフックは玠晎らしく、私たちの生掻を簡玠化したすが、高レベルの汎甚フック状態管理で修正できるいく぀かの欠陥がありたす。 機胜反応ラむブラリmrrのUseMrrが提案され、そのように芋なされたした。







問題ずその解決策









倚くの点で、カスタムフックによっお解決できるず䞻匵するこずができたす。 しかし、これはたさに提案されおいるものですが、異なる実装の代わりに、個別のタスクごずに、党䜓的で䞀貫した゜リュヌションが提案されおいたす。







倚くの問題はあたりに銎染みすぎお、私たちにははっきりず認識されたせん。 たずえば、非同期倉換は垞に同期倉換よりも耇雑で耇雑に芋え、この意味でのフックは以前のアプロヌチedsなどより悪くありたせん。 これを問題ずしお認識するためには、より良い解決策を提䟛する他のアプロヌチを最初に芋る必芁がありたす。







この蚘事は、特定の芋解を課すのではなく、問題に泚意を匕くこずを目的ずしおいたす。 他の゜リュヌションが存圚するか、䟡倀のある代替手段になり埗るが、ただ広く知られおいない他の゜リュヌションが䜜成されおいるず確信しおいたす。 今埌のReact Cache APIも倧きな違いを生むこずができたす。 コメントでの批刀ず議論を歓迎したす。







興味のある方は、3月28日にkyivjsでこのトピックに関するプレれンテヌションを芋るこずができたす。








All Articles