【Error】`Form responses must redirect to another location`のエラー

# はじめに
今回は「診断する」ボタンを押したときに発生したエラーについて。解決までの流れを記事にまとめてみました。

## 環境
- Windows, WSL
- Docker
- Ruby 3.2.3
- Rails 7.1.3



## エラー状況
[![Image from Gyazo](https://i.gyazo.com/c1b1dfe9ef9636af57553d21bd341456.png)](https://gyazo.com/c1b1dfe9ef9636af57553d21bd341456)

1つめのエラー
```
application-69fa277eb9bd4e308e5353b0008b7268a08e7361b5d65e56705847ea8b96f7f4.js:4026 Error: Form responses must redirect to another location
    at _FormSubmission.requestSucceededWithResponse (application-69fa277eb9bd4e308e5353b0008b7268a08e7361b5d65e56705847ea8b96f7f4.js:1641:22)
    at FetchRequest.receive (application-69fa277eb9bd4e308e5353b0008b7268a08e7361b5d65e56705847ea8b96f7f4.js:1389:21)
    at FetchRequest.perform (application-69fa277eb9bd4e308e5353b0008b7268a08e7361b5d65e56705847ea8b96f7f4.js:1367:25)
```

2つめのエラー
```
new:1 Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received
```
調べてみるとどちらも「Rails アプリケーションの JavaScript とフォームの送信に関連」しているそうです。



## 1つ目のエラー: `Form responses must redirect to another location`
### 内容:
このエラーは、フォームを送信した後に、Rails が適切にリダイレクトを行わなかった場合に発生するもので、通常、Rails のフォーム送信は、成功した後に別のページにリダイレクトされる必要があります。

### エラー原因
1. **「診断する」ボタンの押下後、リダイレクトがないこと**フォームが送信された後、ページは通常 `POST` リクエストを送信し、結果を表示するために別の場所にリダイレクトする必要があります。現在、フォームは `POST /posts` を実行していますが、結果を表示するためのリダイレクト処理が不足している可能性がある状態！

👇こちらの記事を参考にしつつも手探りで。

https://qiita.com/qo_op/items/b66e3205d6739d7f7ede


### 解決策：postsコントローラー
1. **リダイレクトを追加する**`create` アクションを追加し、フォームが送信された後、診断結果を表示する `index` アクションにリダイレクトするように変更します

変更前
```
class PostsController < ApplicationController
  def new
    # フォームを表示するためのアクション
  end

  def index
    # パラメータを取得
    question1 = params[:question1]
    question2 = params[:question2]
    question3 = params[:question3]
    question4 = params[:question4]
    question5 = params[:question5]

    # 「はい」（1）の数を合計
    yes_count = [question1, question2, question3, question4, question5].map(&:to_i).sum

    # 結果に基づいて表示する内容を決定
    @result = yes_count

    # 診断結果用にすべてのPostレコードを取得
    @posts = Post.all
  end
end
```

変更後
```
class PostsController < ApplicationController
  def new
    # 新規フォームを表示
  end

  def create
    # フォーム送信後の処理
    redirect_to posts_path(
      question1: params[:question1],
      question2: params[:question2],
      question3: params[:question3],
      question4: params[:question4],
      question5: params[:question5]
    )
  end

  def index
    # パラメータを取得
    question1 = params[:question1]
    question2 = params[:question2]
    question3 = params[:question3]
    question4 = params[:question4]
    question5 = params[:question5]

    # 「はい」（1）の数を合計
    yes_count = [question1, question2, question3, question4, question5].map(&:to_i).sum

    # 結果に基づいて表示する内容を決定
    @result = yes_count

    # 診断結果用にすべてのPostレコードを取得
    @posts = Post.all
  end
end
```

### 解決策：ルーティング
変更前
```
  # トップページ
  root 'tops#index'

  # 診断フォームを表示するルート
  get 'posts/new', to: 'posts#new', as: :new_post

  # 診断結果を表示するためのルート
  post 'posts', to: 'posts#index', as: :posts
```

変更後
```
  # トップページ
  root 'tops#index'

  # 診断フォームを表示するルート
  get 'posts/new', to: 'posts#new', as: :new_post

  # 診断結果を表示するためのルート
  post 'posts', to: 'posts#create', as: :posts

  # 診断結果ページ
  get 'posts', to: 'posts#index', as: :result_posts
```
- `root 'tops#index'`: トップページは `tops#index` アクションを表示します。
- `get 'posts/new', to: 'posts#new', as: :new_post`: 診断フォームのページです（`/posts/new`）。
- `post 'posts', to: 'posts#create', as: :posts`: 診断フォームの送信先です（`POST /posts`）。
- `get 'posts', to: 'posts#index', as: :result_posts`: 診断結果のページにアクセスするルート（`GET /posts`）です。 `create` アクションからこの `index` アクションにリダイレクトされます


### 【`def create` が必要な理由を簡単に】
まず、`def new` と `def index` の役割について
- **`new` メソッド**：新しい診断フォームを表示するページを担当します。このページでユーザーは診断に必要な情報を入力します。`new` は単にフォームを表示するだけで、データを保存したり処理をしたりはしません。
- **`index` メソッド**：診断結果を一覧で表示するページを担当します。このメソッドではすでに保存されたデータ（診断結果など）を取得して、ユーザーに見せます。

次に、**`create` メソッド**がなぜ必要か？
- **`create` メソッド**：診断フォーム（`new` メソッドで表示されるもの）にユーザーが入力したデータを受け取り、サーバーにそのデータを送信して保存する役割を持っています。ユーザーがフォームに入力し「送信」ボタンを押すと、POSTリクエストがサーバーに送られます。このリクエストは`create` メソッドによって処理され、データベースに新しい診断結果が保存されます。


簡単にまとめると：
1. `new` はフォームを表示するだけ（データの保存はしない）。
2. `create` はフォームのデータを保存する（データが保存されないと結果を表示できない）。
3. `index` は保存されたデータを表示するため、`create`でデータが保存されていないと意味がない。

つまり、`new` でフォームを作り、`create`でそのフォームからデータを受け取って保存し、その後`index`で保存された結果を表示する、という流れになります。



## 2つ目のエラー: `A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received`

このエラーは、JavaScript が非同期的に応答を期待しているのに、予期せぬ理由で通信が中断された場合に発生します。通常、Rails の Turbo や Ajax 機能が適切に動作していないことが原因のようで、解決策としては次の3つが挙げられます

### 解決策:
- アプリケーションの JavaScript コードに問題がないか確認します。
- もし Turbo を使っているなら、非同期リクエストに対する正しい応答を処理するようにしてください。
- Turbo を無効にして、通常のフォーム送信を試みることもできます。例えば、フォームタグを次のように変更します。


拡張機能によるものという記事も見られましたがとりあえずはturboに関する部分を見てみました

https://zenn.dev/surume3/articles/d94bbebbae3582

👇Turbo を無効にして、通常のフォーム送信するようにしてみる

変更前
```
# app/views/posts/new.html.erb
<%= form_tag(posts_path, method: :post) do %>
```

変更後
```
<%= form_tag(posts_path, method: :post, data: { turbo: false }) do %>
```


# さいごにまとめ
一応上記の通りに修正することでエラーが解消されたので一安心。
ただ2つ目のエラーに関してはまだ時間がたつといつの間にか発生している状態です。
拡張機能で何か原因があるのかも

今回の記事が何か参考になれば幸いです。