Next.js App Routerにて、サーバー側でfetchした値をReactのContextに渡す方法の覚書

データの取得はサーバー側で1度だけ行いたいが、取得した値はクライアントで参照したい場合のケースについてのメモがあります。APIの呼び出しもクライアント側で行う場合と、サーバー側でキャッシュする場合の2つのコード例が紹介されています。サーバー側でAPI取得をする方法については、propsを使ってコンポーネントに渡すことができることが示されています。また、開発コンソールのネットワークタブでもリクエストは出ていないことが確認できました。

広告ここから
広告ここまで

目次

    「データの取得は、サーバー側で1度だけ行いたい」が、「取得した値はクライアントで参照するので、Contextにいれたい」みたいなケースがあたので調べたメモです。

    素直に書くと、多分こうなるかなと思ったコード

    クライアント側でやるならば、大体こんな感じになるはずです。

    "use client"
    import { FC, PropsWithChildren, createContext, useContext, useState, useEffect } from "react";
    
    
    export const ExampleContext = createContext<{
        posts: object[]
    }>({
        posts: []
    })
    export const useExampleContext = () => useContext(ExampleContext)
    
    export const ExampleContextProvider: FC<PropsWithChildren<{}>> = ({children}) => {
        const [posts, setPosts] = useState([])
        useEffect(() => {
            fetch('https://example.com/dummy-json-api-path')
            .then(data => data.json())
            .then(data => setPosts(data))
        }, [])
        return (
            <ExampleContext.Provider
                value={{
                    posts
                }}
            >
                {children}
            </ExampleContext.Provider>
        )
    }

    ただしこれだと、API呼び出しもクライアント側です。

    microCMSやkintoneなど、API制限もしくはメンテナンス日があるものは、サーバー側でキャッシュしておきたいと思います。

    ContextのProviderではpropだけ受け取る

    いま試しているのは、こんなコードです。

    "use client"
    import { FC, PropsWithChildren, createContext, useContext } from "react";
    
    
    export const ExampleContext = createContext<{
        posts: object[]
    }>({
        posts: []
    })
    export const useExampleContext = () => useContext(ExampleContext)
    
    export const ExampleContextProvider: FC<PropsWithChildren<{
        posts: object[]
    }>> = ({posts, children}) => {
        return (
            <ExampleContext.Provider
                value={{
                    posts
                }}
            >
                {children}
            </ExampleContext.Provider>
        )
    }

    postsをpropsで受け取るようにしました。

    あとは親コンポーネント側でfetchしてやるだけです。

    import Image from 'next/image'
    import { ExampleConsumer, ExampleContext, ExampleContextProvider } from './Context'
    
    export default async function Home() {
      const posts = await fetch('https://example.com/dummy-json-api-path').then(data => data.json())
      return (
        <div>
          <h1>Context area</h1>
          <ExampleContextProvider
            posts={posts}
          >
            <h2>Inner content</h2>
            <ExampleConsumer />
          </ExampleContextProvider>
        </div>
      )
    }

    これでサーバー側でのAPI取得が実現できた・・・はずです。

    開発コンソールの[ネットワーク]タブでも、localhost以外にリクエストを出している様子はありませんでした。

    おわりに

    サーバーサイドの処理もまとめて書けるようになった分、慣れるまではサーバー・クライアント間のデータ受け渡しで「どうやるんだっけ・・・?」ってなる予感がします。

    ただ、今回のサンプルアプリを作ってみた感触として、「子に渡してしまえばただのpropsやね」と考えるのがいい・・・のかもしれません。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark