Rails DM機能修正

## はじめに
前回DM機能実装の記事を書きました。コチラ→（https://qiita.com/YukiyaOgura/items/0e4888c6aad8dc1f9bf9）

実際に何日か使用して、あれ？なんかおかしい、、、ということに気づいて、その違和感の原因と改善方法を記載します。

## 違和感とは
結論から申し上げると、メッセージを送信するたびに再度全メッセージをレンダリングしてました。その結果メッセージの量が増えるたびに動作が非常ーーーーーーに重くなり、送信のたびに画面トップまで戻らされました（毎回全メッセージをビューごとレンダリングする為）

その時のソースコードがこちら。
```ruby:views/chats/_conver.html.erb

<div class="chat-message overflow-auto" style="max-height: 750px;">
  <% chats.each do |chat| %>
<div id="<%= chat.id %>">
  <% if chat.user_id == current_user.id %>
    <div class="message self <%= 'no-bg' if chat.image.attached? || chat.video.attached? %>">
      <div class="message-body">
        <%= chat.message %>

        <% if chat.image.attached? %>
          <div class='file-container'>
            <%= image_tag(chat.image.variant(resize_to_limit: [500, 500]), width: '100%', height: 'auto', class: 'rounded') %>
            <%= button_to '保存', saved_files_path(chat_id: chat.id), method: :post, remote: true, class: 'save-btn' %>
             <span id="status-<%= chat.id %>" class="status" style="display: none;">保存済み</span>
          </div>
        <% end %>

        <% if chat.video.attached? %>
          <div class='file-container'>
            <%= video_tag rails_blob_path(chat.video), controls: true, class: 'responsive-video rounded' %>
            <%= button_to '保存', saved_files_path(chat_id: chat.id), method: :post, remote: true, class: 'save-btn' %>
            <span id="status-<%= chat.id %>" class="status" style="display: none;">保存済み</span>
          </div>
        <% end %>
      </div>

      <% if chat.read %>
        <span class="read">既読</span>
      <% end %>

      <p class="chat-timestamp"><%= chat.created_at.strftime("%Y-%m-%d %H:%M")%></p>
    </div>
  <% else %>
    <div class="message other <%= 'no-bg' if chat.image.attached? || chat.video.attached? %>">
      <div class="message-sender"><%= chat.user.username %></div>
      <div class="message-body">
        <%= chat.message %>

        <% if chat.image.attached? && chat.user_id != current_user.id %>
          <div class="file-container">
            <%= image_tag(chat.image.variant(resize_to_limit: [500, 500]), width: '100%', height: 'auto', class: 'rounded') %>
            <%= button_to '保存', saved_files_path(chat_id: chat.id), method: :post, remote: true, class: 'save-btn' %>
            <span id="status-<%= chat.id %>" class="status" style="display: none;">保存済み</span>
          </div>
        <% end %>

        <% if chat.video.attached? && chat.user_id != current_user.id %>
          <div class="file-container">
            <%= video_tag rails_blob_path(chat.video), controls: true, class: 'responsive-video rounded' %>
            <%= button_to '保存', saved_files_path(chat_id: chat.id), method: :post, remote: true, class: 'save-btn' %>
            <span id="status-<%= chat.id %>" class="status" style="display: none;">保存済み</span>
          </div>
        <% end %>
      </div>
      <p class="chat-timestamp"><%= chat.created_at.strftime("%Y-%m-%d %H:%M")%></p>
       </div>
  <% end %>
</div>
```

```ruby:views/chats/create.js.erb
$('.chat-message').append("<%= j(render 'newchat', chat: @chat) %>");
$('input[type=text]').val("");
```

`$('.message').html("<%= j(render 'chats/conver', chats: @chats) %>");`
この行は、ページ内のclass属性が'message'の要素のHTML内容を更新しています

`<%= j(render 'chats/conver', chats: @chats) %>`

`chats/conver`
パーシャルをレンダリングし、その結果をJSON形式にエスケープ（jはescape_javascriptのエイリアス）しています。結果的に、この行は新たに取得したチャットメッセージをページに表示するためのものです。

`$('input[type=text]').val("");`
この行は、ページ内の全てのテキスト入力フィールド（input[type=text]）の値を空文字列にリセットしています。これは、ユーザーがメッセージを送信した後、入力フィールドをクリアするためのものと思われます。

はい。以上が今までのコードです。


## 改善後のコード

まずは新しく部分テンプレートを作成します。
`app/views/chtas/_newchat.html.erb`としましょう。

そしたら`chats/_conver.html.erb`のメッセージの表示部分を切り取りましょう。

```ruby:views/chats/_newchat.html.erb
<div id="<%= chat.id %>">
  <% if chat.user_id == current_user.id %>
    <div class="message self <%= 'no-bg' if chat.image.attached? || chat.video.attached? %>">
      <div class="message-body">
        <%= chat.message %>

        <% if chat.image.attached? %>
          <div class='file-container'>
            <%= image_tag(chat.image.variant(resize_to_limit: [500, 500]), width: '100%', height: 'auto', class: 'rounded') %>
            <%= button_to '保存', saved_files_path(chat_id: chat.id), method: :post, remote: true, class: 'save-btn' %>
             <span id="status-<%= chat.id %>" class="status" style="display: none;">保存済み</span>
          </div>
        <% end %>

        <% if chat.video.attached? %>
          <div class='file-container'>
            <%= video_tag rails_blob_path(chat.video), controls: true, class: 'responsive-video rounded' %>
            <%= button_to '保存', saved_files_path(chat_id: chat.id), method: :post, remote: true, class: 'save-btn' %>
            <span id="status-<%= chat.id %>" class="status" style="display: none;">保存済み</span>
          </div>
        <% end %>
      </div>

      <% if chat.read %>
        <span class="read">既読</span>
      <% end %>

      <p class="chat-timestamp"><%= chat.created_at.strftime("%Y-%m-%d %H:%M")%></p>
    </div>
  <% else %>
    <div class="message other <%= 'no-bg' if chat.image.attached? || chat.video.attached? %>">
      <div class="message-sender"><%= chat.user.username %></div>
      <div class="message-body">
        <%= chat.message %>

        <% if chat.image.attached? && chat.user_id != current_user.id %>
          <div class="file-container">
            <%= image_tag(chat.image.variant(resize_to_limit: [500, 500]), width: '100%', height: 'auto', class: 'rounded') %>
            <%= button_to '保存', saved_files_path(chat_id: chat.id), method: :post, remote: true, class: 'save-btn' %>
            <span id="status-<%= chat.id %>" class="status" style="display: none;">保存済み</span>
          </div>
        <% end %>

        <% if chat.video.attached? && chat.user_id != current_user.id %>
          <div class="file-container">
            <%= video_tag rails_blob_path(chat.video), controls: true, class: 'responsive-video rounded' %>
            <%= button_to '保存', saved_files_path(chat_id: chat.id), method: :post, remote: true, class: 'save-btn' %>
            <span id="status-<%= chat.id %>" class="status" style="display: none;">保存済み</span>
          </div>
        <% end %>
      </div>
      <p class="chat-timestamp"><%= chat.created_at.strftime("%Y-%m-%d %H:%M")%></p>
       </div>
  <% end %>
</div>
```

次に`chats/_conver.html.erb`に`chats/_newchat.html.erb`をレンダリングしましょう。

```ruby:views/chats/_conver.html.erb
<div class="chat-message overflow-auto" style="max-height: 750px;">
  <% chats.each do |chat| %>
   <%= render 'newchat', chat: chat %>
   <% end %>
</div>
```

すっきりしましたね。

では最後に`chats/create.js.erb`の記述も変えます。
```ruby:views/chats/create.js.erb
#.chat-messageクラスを持つエレメントを参照、.appendで選択された内部に新しいHTMLを追加(<%#=%>~~)
$('.chat-message').append("<%= j(render 'newchat', chat: @chat) %>");
#ID:chat_messageを持つformを空にする
$('#chat_message').val("");
#name="chat[image]"を持つフォームを空にする-
$('name="chat[image]"').val("");
#name="chat[video]を持つフォームを空にする"
$('name="chat[video]"').val("");

```

非同期通信化の特徴として、入力された情報がフォームに残ってしまうというのがあります。逆に同期通信の場合は全てのフォールがクリアになります。
なのでメッセージや画像、動画といったファイルを送信した後、非同期通信の場合は、フォームがそのまま残ってしまうということになりますので必ずこの記述が必須になります。

以上で完了です。

## 解説
もともと`$('.message').html("<%= j(render 'chats/conver', chats: @chats) %>");`というコードがありました。これは、サーバーから受け取った全てのチャット`（@chats）`をレンダリングし、それを`.message`というクラスを持つHTML要素の内部に挿入するものです。つまり、チャットが追加されるたびに全てのチャットメッセージが再レンダリングされ、表示されていました。

新しいコードは
`$('.chat-message').append("<%= j(render 'newchat', chat: @chat) %>");`では、新たに追加されたチャットメッセージ`（@chat）`だけがレンダリングされ、それが`.chat-message`というクラスを持つHTML要素の末尾に追加されます。つまり、新たなチャットが追加されるたびにそのチャットだけがレンダリングされ、既存のチャットメッセージはそのまま保持されます。これにより、パフォーマンスが向上し、新しいチャットが追加された時に既存のチャットが影響を受けなくなりました。

`("<%= j(render 'newchat', chat: @chat) %>");`について
`chat: @chat`はコントローラからビューに渡す時に使用される記述です。
ここで`@chat`はコントローラで記述した新しく作成されたチャットメッセージのデータが入っているインスタンス変数です。
```ruby:views/chats/controller.rb
@chat = Chat.new(room_id: @room.id)#チャットの投稿
```
これのことですね。

このコードが実行される際、`<%= j(render 'newchat', chat: @chat) %>`の部分で、`newchat`という名前の部分テンプレートが呼び出され、`_newchat.html.erb`ファイルがレンダリングされます。

この部分テンプレートでは、`chat: @chat`から渡された`chat`という名前のローカル変数を使用して、新しく作成されたチャットメッセージの内容を表示します。

したがって、`chat: @chat`は`_newchat.html.erb`という部分テンプレートに`@chat`というインスタンス変数を`chat`という名前で渡しているということになります。

```ruby:views/chats/_conver.html.erb
<div class="chat-message overflow-auto" style="max-height: 750px;">
  <% chats.each do |chat| %>
   <%= render 'newchat', chat: chat %> #ここのこと
   <% end %>
</div>
```

その結果、`chats`配列内の各要素である`chat`ごとに部分テンプレートが呼び出され、それぞれのメッセージが表示されます。これにより、全てのメッセージを一度に再表示するのではなく、個別のメッセージを追加するたびに最新のメッセージのみがビューの一番下に表示されるようになります。


以上ですが、ちょっとした書き方の違いでものすごい差ができることに感動しました。

コメント、いいねお待ちしてます！！