データが「消えた」?と思ったら…
ある日、作業履歴を確認していると、古いデータが表示されなくなっていました。
でも、Supabaseのデータベースを確認すると、データはちゃんと残っています。
「データはあるのに、画面に出ない」
そういえば、開発初期に仮で入れた limit をすっかり忘れていただけでした。
.order("log_date", { ascending: false })
.order("created_at", { ascending: false })
.limit(100)
新しい100件だけ取得していた、という状態です。
もう、ログが100件を超えたのか・・・と思っていたのですが、limit(100) で取得件数を絞ったままにすると過去の履歴が見えない。制限を外して全件表示にしたとしても、今度はひたすら下にスクロールしないといけない。それはそれで使いにくい。
と、いうことでlimitを外してフロントでページネーション
limit を外すことにしましたが、次に考えたのはどの単位でページネーションするかです。
このアプリでは画面が日付ごとにグループ表示されているため、ログ件数で区切ると「同じ日付が2ページにまたがる」という問題が起きます。
例えば、下記のような作業があったとき

ユーザーが見ている単位は「1ログ」ではなく「1日分のまとまり」です。
もしDB側で100ログずつ区切ると、こんな問題が起きます。
例:各日のログ数がこうだった場合
| 日付 | ログ数 | 累計 |
|---|---|---|
| 2/16 | 10件 | 10件 |
| 2/17 | 8件 | 18件 |
| 2/18 | 85件 | 103件 |
100件目の境目で、2月18日の途中でページが切れます。同じ日付が1ページ目と2ページ目に分断されてしまいます。
そこで、フロント側で日付グループ単位のページネーションを実装することにしました。
今回の仕様
- 1ページ = 20日分(日付グループ単位)の履歴を表示
- 20日分を超えると「次へ」ボタンを表示
実装コード
まずページ管理のstateとページあたりの件数を定義します。
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 20;
次に、useMemo を使って現在のページに表示するデータを切り出します。
const paginatedWorksByGroup = useMemo(() => {
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
return worksByGroup.slice(startIndex, endIndex);
}, [worksByGroup, currentPage]);
総ページ数の計算はこうなります。
const totalPages = Math.ceil(worksByGroup.length / itemsPerPage);
これで21日分のデータがあれば、自動的に「1 / 2」「次へ」ボタンが表示されます。

フィルタ・カレンダーとの整合性
ページネーションを追加したとき、既存のフィルタ・カレンダー機能への影響が気になりました。
結果として:
- フィルタ:正常に動作(ページネーションと独立して機能)
- カレンダー:全期間の作業日を表示(ページネーションの影響なし)
ページネーションは「一覧の表示件数」にのみ影響し、カレンダーは「全ログ」を元に表示しています。これは結果的に自然な挙動でした。
今の設計と、将来的な改善案
今の設計(現状)
現在のログ数はそれほど多くないため、こういう方針にしています。
- Supabaseから全ログを一括取得
- フロントで集計・グループ化
- フロントでページネーション
シンプルで問題ありません。
将来的にログが増えた場合
ログが数千件になったとき、全件取得は重くなります。そのときは以下のように分離する予定です。
一覧表示
// ページ単位でサーバーから取得
.from("cult_logs")
.select("*")
.range(startIndex, endIndex)
カレンダー
// 月単位で日付だけ取得
.from("cult_logs")
.select("log_date")
.gte("log_date", "2026-02-01")
.lte("log_date", "2026-02-28")
取得データが小さくなり、パフォーマンスが安定し、責務も分離できます。今は不要ですが、設計として頭に入れておくと後で困りません。
現在と今後ののアプローチの違いをまとめるとこうなります。
| 比較 | DBページネーション(件数単位) | 今回の実装(日付単位) |
|---|---|---|
| どこで処理するか | サーバー(Supabase) | ブラウザ(フロント) |
| ページの区切り | ログ100件ごと | 日付20日分ごと |
| 日付をまたぐ問題 | 発生する | 発生しない |
| データ量が増えたとき | 軽い | 重くなる可能性あり |
一言でいうと、「誰が・何を基準に切るか」が違います。
まとめ
今回やったこと:
limit制限を外してページネーションを実装- 1ページ20日分の表示に変更
- フィルタ・カレンダーとの整合性を確認
