👌

Rails+Turboで無限スクロールをさくっと作る

2024/03/12に公開

以下記事にてTurboを使用した無限スクロールの例が面白かったので実際に作りながら確認してみました。
https://buttondown.email/bhumi/archive/building-infinite-scroll-with-turbo-frames-and

事前準備

RubyやRailsに関しては以下バージョンです

Ruby: 3.3.0
Rails: 7.1.3

tailwindcss入れておくとscaffoldの画面が綺麗に見えるのでそれは入れてその他はデフォルトのままにします。

$ rails new infinite_scroll -c tailwind
$ cd infinite_scroll
$ bin/rails tailwindcss:install

scaffoldとseedsを使って適当なデータを作成します。

$ bin/rails g scaffold Post content:text
$ bin/rails db:migrate

あとはseedsデータを準備します。ダミーテキストは夏目漱石のこころから。
長さ変わったほうがわかりやすいかなということで適当にsliceしてます。

db/seeds.rb
text = "伯父も首を傾けました。しかも私の受けたその時の私がもしこの驚きをもって、その墓を見た時は、もう帰りませんでした。私はそれを緒口にまた話を始めました。あなたのお父さんが亡くなられるのを、ちょうど嫁でも貰った。彼の眼の前に立っていましたから、普通の人間として、私はそんな上の空でいってる事じゃないんだと私は見込んでいたのです。そうして八畳の中をあちらこちらと泳いでいるのだろうか。幸いにKの黒い姿はそこに私の返事を出そうかと考えて、かえって変な反撥力を感じた。先生が奥さんと話していた。潔癖な父は、単なる娯楽の相手というのです。必竟やくざだから遊んでいるので、ほとんど新聞を読む暇がなかった。"

100.times do |i|
  Post.create!(content: text.slice(0, rand(1..100)))
end

seedsの変更が終わったのでDBに入れます。

$ bin/rails db:seed

次にページネーションを実装するgemを入れます。元記事ではpagyを入れていますが今回は個人的な好みでkaminariを入れます。

$ bundle add kaminari

無限スクロールを作る

controllerから触っていきます。さきほどkaminariを入れたのでkaminariに適応させましょう。

app/controllers/posts_controller.rb
def index
-  @posts = Post.all
+  @posts = Post.all.page(params[:page]).per(10)
end

controllerができたので次はviewを触ります。
render @posts になっている部分を修正していきます。

app/views/posts/index.html.erb
-   <%= render @posts %>
+   <%= turbo_frame_tag "posts_page_#{@posts.current_page}" do %>
+      <%= render @posts %>
+
+      <% if @posts.next_page %>
+        <%= turbo_frame_tag "posts_page_#{@posts.next_page}",
+          src: posts_path(page: @posts.next_page),
+          loading: :lazy
+        %>
+      <% end %>
+    <% end %>

これでできました!早い!サーバーを立ち上げて見てみましょう。
下にスクロールすればどんどん読み込みが進んで無限スクロールが実装できているかと思います。

仕組みとしては loading="lazy"が指定されていることによってスクロールが下のほうに近づいたタイミングで posts_path(page: @posts.next_page) へアクセスされます。
posts#indexのviewの中には次ページのID(posts_page_2など)を含んだturbo-frameが含まれているのでその部分が置き換えされて次のページへアクセスできます。
それがデータの終わりまでアクセスされるので無限スクロールが出来る、ということになります。
<turbo-frame>が入れ子構造になっていくので形として面白いです。

これだけで無限スクロールが出来るのはとても便利なのですが少し問題もあります。
例えば別ページのものを表示してブラウザの「戻る」をクリックするとturbo-frameで読み込まれたものがすべてリセットされてしまう問題があります。
なのでこれを使うのであれば「戻る」を想定していない場所で使うのがいいかなと思います。

Discussion