nino
  • Docs
  • Registry
  • Membership

Command Palette

Search for a command to run...

スポンサー
メニュー

中規模のリストをブラウザにキャッシュし、クライアントでフィルタする

 

2m 58s

中規模かつ更新頻度の低いデータを検索する場合、Cache API で結果をキャッシュし、ブラウザで fuse.js を使って検索することでデータリクエストを減らし、高速な検索を実現できます。

ライブデモ

概要

このアプローチは以下のような場合に有効です:

  • 中規模のデータ: 数百〜数千件程度のリスト
  • 更新頻度が低い: データが頻繁に変更されない
  • 検索が頻繁: ユーザーが頻繁に検索操作を行う
  • ネットワークリクエストを減らしたい: サーバーへの負荷を軽減したい

メリット

  • 高速な検索: ブラウザ内で検索するため、ネットワークレイテンシが発生しない
  • オフライン対応: 一度キャッシュされれば、オフラインでも検索可能
  • サーバー負荷の軽減: 検索リクエストがサーバーに到達しない
  • ユーザー体験の向上: 即座に検索結果が表示される

必要な依存関係のインストール

Loading...

Cache API 用のヘルパー関数を作成

Loading...

リストコンポーネントを作成

Loading...

検索用フォームを作成

Loading...

画面

Loading...

データ更新時のバージョン更新

データの追加、削除、更新時に必ずキャッシュのバージョンを更新します。

Loading...
Discord で質問する
何か気になったこと、分からないことがあれば気軽に質問してください!
DiscordDiscordで質問する
nino

Developer

XXGitHubGitHubYouTubeYouTubeZennZennDiscordDiscord

    Links

  • Documentation
  • Registry
  • Architecture

    Tools

  • Feed
  • Status

    Policies

  • Terms of Service
  • Privacy Policy
  • Legal
Documentation
Getting Started
  • Changelog
Guides
  • Webアプリの環境構築
  • ニュース収集&AI要約
  • Turso のテーブル移行
  • 日時の管理
  • 中規模のリストをブラウザにキャッシュし、クライアントでフィルタする
  • Search Params Dialog
  • コードの整理
  • AGENTS.md
  • Better Auth
  • プロダクト開発ポリシー
  • 推奨ツール
  • Proxy.ts
  • 多言語対応
  • SWR
  • Cache Component
  • Next.js の課題、不具合
  • アプリ開発フロー
  • プロンプトガイド
  • CSS Tips
  • Font Family
  • セルフブランディング
  • セルフオンボーディング
  • オフィスツール
  • Email
  • Breadcrumb(パンくず)
  • 並べ替え
npm install fuse.js nuqs
export const getCachedData = async <T>(
  cacheName: string,
  url: string
): Promise<T | false> => {
  const cacheStorage = await caches.open(cacheName);
  const cachedResponse = await cacheStorage.match(url);

  if (!cachedResponse || !cachedResponse.ok) {
    return false;
  }

  return (await cachedResponse.json()) as T;
};

export const deleteOldCaches = async (keyPrefix: string) => {
  const keys = await caches.keys();
  for (const key of keys) {
    const isOurCache = key.startsWith(keyPrefix);
    if (!isOurCache) {
      continue;
    }
    caches.delete(key);
  }
};

export async function getCacheVersion() {
  const cachedData = await db.query.todoSettings.findFirst();
  if (!cachedData) {
    return 1;
  }
  return cachedData.version;
}

export async function updateCacheVersion() {
  const cachedData = await db.query.todoSettings.findFirst();
  if (!cachedData) return;
  const newVersion = cachedData.version + 1;
  await db.update(todoSettings).set({ version: newVersion });
}
"use client";

import { useEffect, useMemo, useState } from "react";
import { getTodos } from "./data";
import { Todo } from "./type";
import { useQueryState } from "nuqs";

export function TodoList() {
  const [todos, setTodos] = useState<Todo[] | null>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [search] = useQueryState("q", {
    defaultValue: "",
  });

  useEffect(() => {
    getTodos().then((todos) => {
      setTodos(todos);
      setIsLoading(false);
    });
  }, []);

  const filteredTodos = useMemo(() => {
    return todos?.filter((todo) => todo.title.includes(search));
  }, [todos, search]);

  if (isLoading) {
    return <div>Loading...</div>;
  }

  return (
    <ul>
      {filteredTodos?.map((todo) => (
        <li key={todo.id}>
          {todo.id}_{todo.title}
        </li>
      ))}
    </ul>
  );
}
"use client";

import { Input } from "@/components/ui/input";
import { useQueryState } from "nuqs";

export function SearchForm() {
  const [search, setSearch] = useQueryState("q", {
    defaultValue: "",
  });

  return (
    <Input
      type="text"
      placeholder="Search"
      value={search}
      onChange={(e) => setSearch(e.target.value)}
    />
  );
}
import { Suspense } from "react";
import { SearchForm } from "./search-form";
import { TodoList } from "./todo-list";

export default function BrowserCacheClientFilterPage() {
  return (
    <div className="space-y-4 container py-10">
      <h1 className="text-2xl font-bold">Todo List</h1>
      <Suspense>
        <SearchForm />
        <TodoList />
      </Suspense>
    </div>
  );
}
"use server";

async function updateTodos() {
  // タスク更新処理

  await updateCacheVersion();
}