Freshで全ページ共通のサイドバーを作る

作成日
更新日

ポイント

  • _layout.tsx には Handler は定義できない
    • しかしDeno Fresh のレイアウトで非同期処理してる!
    • これはサーバーサイドの処理らしい。
    • これでよくね?
  • _app.tsx にも Handler は定義できない
  • _middleware.tsx から _app.tsx に持っていくことはできるけど本来の使い方ではない

共通化できる部分

  • _app.tsx で共通 Sidebar を組み込む
  • Sidebar.tsx コンポーネントを用意する

全てのページで書かないといけない?

  • 各ページで共通データを注入
  • この書いたデータが _app.tsx から取得することができる。
export const handler: Handlers = {
  async GET(_req, ctx) {
    const common = await getCommonPageData();
    return ctx.render({ ...common });
  },
};

routes/_layout.tsx について

routes/_layout.tsx ファイルを用意していたけれど、このファイルの役割は index.tsx ページと about.tsx ページといった routes の配下に適用される。

次の _layout.tsx ファイルをみつけたら、そちらが優先される。 routes/posts/_layout.tsx があれば routes/posts/index.tsx では routes/_layout.tsx が使われる。

~~なので、基本的に routes/_layout.tsx は作らないでも良いかもしれない。全てのページで共通化したいコンポーネントは _app.tsx にかくのが適切そう。

そんなことないかも。

最終

これでいいじゃん。

route/_layout.tsx
import Footer from "../components/Footer.tsx";
import { FreshContext, PageProps } from "$fresh/server.ts";
import { getCommonPageData } from "../utils/getCommonPageData.ts";
import Navigation from "../components/Navigation.tsx";

export default async function Layout(req: Request, ctx: FreshContext) {
  const common = await getCommonPageData();
  const postMap = common.postMap;

  return (
    <>
      <div class="flex flex-1 w-full">
        {/* サイドバー(PCのみ) */}
        <aside class="hidden lg:flex flex-col w-64 h-screen fixed top-0 left-0 border-r border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 z-40">
          <Navigation postMap={postMap} />
        </aside>
        <div class="flex-1 lg:ml-64 flex flex-col min-h-screen justify-between">
          <div class="w-full p-8">
            <ctx.Component />
          </div>
          <div class="order-last">
            <Footer />
          </div>
        </div>
      </div>
    </>
  );
}

コレにより

  • 各ページで共通データを注入なんてしなくて良い

結局用意した _layout.tsx ファイルは

  • routes/_layout.tsx トップページ用
  • routes/[category]/_layout.tsx 単一記事用
  • routes/search/_layout.tsx 検索結果画面用

Fresh 1.2 へアップグレード - island の新機能など シグナルとアイランドを使って、どうにかページが変わった時に再読み込みされないサイドバーを作りたい。

パーシャルを使う

  • 全体で partials を有効化する

全体で Partial を有効化する

bodyf-client-nav を追加する。

routes/_app.tsx
export default function App(props: FreshContext) {
  return (
    <html data-theme={theme}>
    {/* body 内でパーシャルを有効化する */}
      <body class="bg-background" f-client-nav> 
        <Component />
      </body>
    </html>
  );
}

記事部分を Partial で囲う

記事のコンテンツ部分をPartialで囲う。この部分は変更される部分になる。

/routes/_layout.tsx
import Footer from "../components/Footer.tsx";
import { FreshContext } from "$fresh/server.ts";
import { getCommonPageData } from "../utils/getCommonPageData.ts";
import Navigation from "../components/Navigation.tsx";
import Header from "../components/Header.tsx";
import { Partial } from "$fresh/runtime.ts";

export default async function Layout(req: Request, ctx: FreshContext) {
  const common = await getCommonPageData();
  const postMap = common.postMap;

  return (
    <>
      <div class="flex flex-1 w-full">
        <Header />
        {/* サイドバー(PCのみ) */}
        <aside class="hidden lg:flex flex-col w-64 h-screen fixed top-0 left-0 border-r border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 z-40">
          <Navigation postMap={postMap} />
        </aside>
        <div class="flex-1 lg:ml-64 flex flex-col min-h-screen justify-between">
          <div class="w-full p-8">
            <Partial name="content"> {/* ここに Partial を追加 */}
              <ctx.Component />
            </Partial>
          </div>
          <div class="order-last">
            <Footer />
          </div>
        </div>
      </div>
    </>
  );
}

これで一応サイドバーは画面遷移後もそのままになった。

f-partial

リンクに f-partial をつけるって書いてるけど、つけると動かなくなる。nameが違うのかな?

<a
href={`/${category.name}/${post.slug}`} 
class="text-blue-600 hover:underline"
f-partial="content"
>
{post.title}
</a>

これはよくわからん。けど書かなければちゃんと動く。何指定するのかわからん...

結論

Deno Fresh のパーシャルを使おう!

サイトアイコン
公開日
更新日