🎀

【Next.js 13】Client Componentで`use`フックを使う

2022/10/29に公開

タイミーでフロントエンドエンジニアをしているnisshi-です。

今回はNext.js 13のクライアントコンポーネントにおいて、useフックを利用する基本的な流れを紹介したいと思います。

Server Componentとuseフック

Next.js 13において、コンポーネントはサーバー側でレンダリングされるサーバーコンポーネントがデファクトになりました。

一方、
先日Reactに追加されたuseフックは、再レンダリングが走らないサーバーコンポーネントでは特に意識せずに利用する事が出来ますが

page.tsx
import { use } from 'react'

async function getTodoList() {
  // https://jsonplaceholder.typicode.com/
  const response = await fetch('https://jsonplaceholder.typicode.com/todos');
  const result = await response.json();
  return result;
}

export default function TodoListPage() {
  const todos = use(getTodoList());
  
  return (
   <table>
     {/* ...省略... */}
     <tbody>
       {todos.map((todo) => (
         <tr key={todo.id}>
           <td>{todo.userId}</td>
           <td>{todo.id}</td>
           <td>{todo.title}</td>
         </tr>
       ))}
     </tbody>
   </table>
  )
}

クライアントコンポーネントで下記の様にuseフックを使うと無限レンダリングが発生します。

page.tsx(無限レンダリングが起こる)
+ 'use client'

import { use } from 'react'

async function getTodoList() {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos');
  const result = await response.json();
  return result;
}

export default function TodoListPage() {
  const todos = use(getTodoList()); 
  
  // ↓!無限にレンダリングされる!
  return (
   <table>
     {/* ...省略... */}
     <tbody>
       {todos.map((todo) => (
         <tr key={todo.id}>
           <td>{todo.userId}</td>
           <td>{todo.id}</td>
           <td>{todo.title}</td>
         </tr>
       ))}
     </tbody>
   </table>
  )
}

何故このような事が起こるのか

useフック自体には、SWRTan Stack Queryの様にキャッシュの責務が無い為、再レンダリングが発生しうるクライアントコンポーネント上ではPromise解決 -> レンダリング -> Promise解決.....というループに陥るためです。

このあたりは@uhyo様が詳しくまとめられているので、そちらの記事も併せてご確認頂ければ幸いです。
https://zenn.dev/uhyo/articles/react-use-rfc

どうすれば良いか

useと同じく、先日Reactに追加されたcacheを利用します。

page.tsx
import { cache } from 'react'

const getTodoList = cache(async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos');
  const result = await response.json();
  return result;
});

fetcherとなる関数をcacheで包めば、戻り値がメモ化され、呼び出し側のコンポーネントの再レンダリングが抑止されます。

cacheの詳細に関しては↓をご覧ください。
https://beta.nextjs.org/docs/data-fetching/caching

最後に

以上がusecacheを利用する基本的な流れになります。

サーバーコンポーネントがデファクトになっているNext 13ですが、実務においてはクライアントコンポーネントとサーバーコンポーネントのコラボレートが発生しうるケースは多いと考えています(例 : ユーザー入力をトリガーにしたインクリメンタル検索等)

usecacheを適切に使い、ReactとNext.jsのアップデートを引き続き最大限楽しみましょう。

また、タイミーではフロントエンド始め、エンジニアを積極的募集中でした。

もしご興味を持って頂けましたら、是非一度下記をご確認頂けますと幸いでした。
https://timee.notion.site/timee/Timee-Product-Org-Entrance-Book-b7380eb4f6954e29b2664fe6f5e775f9

それではまた別の記事で。

Discussion