はじめに
個人開発中のタスク管理アプリで、Turbo Streamを利用した非同期投稿機能を実装していました。
しかし新規ユーザーで初めてタスクを投稿すると、
- DBには保存される
- リロードすると表示される
- しかし投稿直後は「タスクがまだありません」のまま
という不思議な現象が発生しました。
今回はその原因調査から解決までの流れをまとめます。
発生していた現象
新規ユーザーの状態でタスクを投稿すると、
タスクがまだありません
が表示されたまま。
しかしページをリロードすると投稿したタスクが表示されます。
つまり、
- 保存処理は成功
- Turbo Stream更新だけ失敗
という状況でした。
最初に疑ったこと
まずTurbo Streamのレスポンスが返っているか確認しました。
tail -n 100 log/development.log
結果は正常。
Rendering tasks/create.turbo_stream.erb
Completed 200 OK
Turbo Stream自体は返されていました。
Chrome DevToolsで確認
Consoleで確認。
document.getElementById("tasks")
結果
null
本来存在するはずの
<div id="tasks">
がDOM上に存在していませんでした。
原因
index.html.erbで
<% if @tasks.any? %>
<div id="tasks">
...
</div>
<% else %>
タスクがまだありません
<% end %>
としていたためです。
初回アクセス時は
@tasks.empty?
なので、
<div id="tasks">
自体が生成されません。
しかしTurbo Streamでは
<turbo-stream action="replace" target="tasks">
で置換しようとしていたため、
置換対象が存在せず反映されませんでした。
修正
空でも常にtasksコンテナを描画するよう変更。
<div id="tasks">
<%= render partial: "tasks/task", collection: @tasks %>
</div>
<% if @tasks.empty? %>
<div id="empty-message">
タスクがまだありません
</div>
<% end %>
これでTurbo Streamが正常に置換できるようになりました。
さらに発生した問題
修正後、
- 「タスクがまだありません」が消えない
- モーダルが2回目以降小さくなる
- リロードすると空のモーダルが表示される
という別の問題も発生。
原因はTurbo Streamとモーダル制御(Stimulus)の競合でした。
モーダルを閉じる処理を整理し、
closeModal()
でwrapper側の表示制御を行うよう修正。
学んだこと
Turbo Streamを使うときは、
置換対象のDOMが常に存在すること
が重要。
また、
document.getElementById(...)
や
document.body.innerHTML.includes(...)
を使ったDOM確認が非常に有効でした。
おわりに
今回の調査では、
- Railsログ確認
- Turbo Stream確認
- DevToolsによるDOM調査
- Stimulusの挙動確認
を行いながら原因を切り分けました。
「DBには保存されるのに画面が更新されない」という問題に遭遇した方の参考になれば嬉しいです。