Concurrent Modeのドキュメントを読みました。useEffect
によるFetch-on-render
から、Suspense
によるRender-as-You-Fetch
への変化が本質にみえます。
before
function ProfilePage() { const [user, setUser] = useState(null); useEffect(() => { fetchUser().then(u => setUser(u)); }, []); if (user === null) { return <p>Loading profile...</p>; } return ( <> <h1>{user.name}</h1> <ProfileTimeline /> </> ); } function ProfileTimeline() { // 省略 }
after
// This is not a Promise. It's a special object from our Suspense integration. const resource = fetchProfileData(); function ProfilePage() { return ( <Suspense fallback={<h1>Loading profile...</h1>}> <ProfileDetails /> <Suspense fallback={<h1>Loading posts...</h1>}> <ProfileTimeline /> </Suspense> </Suspense> ); } function ProfileDetails() { // Try to read user info, although it might not have loaded yet const user = resource.user.read(); return <h1>{user.name}</h1>; } function ProfileTimeline() { // Try to read posts, although they might not have loaded yet const posts = resource.posts.read(); return ( <ul> {posts.map(post => ( <li key={post.id}>{post.text}</li> ))} </ul> ); }
いいですね。if (user === null) {
が消えています。読みやすいですね。でも、ひとつ疑問があります。resource.user.read()
これはなんなのか。
Facebookの答えはRelayです。Relay is a JavaScript framework for building data-driven React applications powered by GraphQL
だそうです。useLazyLoadQuery
が重要なhookになります*2。とはいえ、Relayそのものを使えという話ではありません。サーバーとクライアントのお喋りをいい感じにやる君が必要なだけです。後ろにservice workerがいるのかもしれませんし、in-memoryでキャッシュしているかもしれません。もっと素朴に作るなら、Lazy Loadとキャッシュ機構抜きで、データのfetch
と加工を隠蔽してくれれば大丈夫です。
Suspense
を使ってとりあえずrenderされるUIを作るには、賢めのデータとる君が必要になりそうです。自作をしても良いですし、Relayのようなフレームワークに乗っかってもよいです。最悪なのは、resource.user.read()
の行でfetch
し始めることです。UI定義の中でエラーハンドリング、データの変形をやり始めると、Presentation Domain Separation*3から遠ざかりますし、何より読みにくくなります。
元はといえば、useEffect
の時からそうでした*4。何でも非同期処理を書けるのでuseEffect
は割れ窓にできます。componentDidMount
の時代から可能だったと言えばそう。Facebookにいる人々のレベルが高いからかもしれませんし、React is a JavaScript library for building user interfaces
の責務に忠実だから危険なほどの自由さがあるのかもしれません。Reactの各種APIはとても便利にできています。プログラマは柔軟にUIを構築できます。ですが、Reactでメンテナンスしやすいコードを書くには(そこそこ深く)考え続けなければなりません。自由とはそういうものですし、制約の少ないフレームワークにするのがReactのやり方なのでしょう。私は自由と考え事が多いものは好みですが、チームで開発すると苦労は増えます。
*5
Reactは着実に進化しており、ますます柔軟にUIを作れるようになっています。Presentation Domain SeparationとReactの自由さ、開発者のスケーラビリティすべて満たすのはたいへんそうだなと思ったのでした。
*1:https://reactjs.org/docs/concurrent-mode-suspense.html#approach-1-fetch-on-render-not-using-suspense より引用
*2:https://relay.dev/docs/en/experimental/api-reference#uselazyloadquery
*3:https://martinfowler.com/bliki/PresentationDomainSeparation.html
*4:useEffectが陳腐化したわけではありません。今後は両方を使い分けることになります。
*5:宣伝です: ねずみのすきま - BOOTH