[Rails]パーシャル内パーシャルのrender繰り返しを防ぐには

# ハマった点

投稿一覧ページに「いいねボタン」を設置する際、各投稿のパーシャルをrenderする際の繰り返しは、collectionオプションにて解決できた。 

しかし、各投稿のパーシャルの中でrenderしているパーシャルにおいては、投稿の分だけrenderの繰り返しが発生してしまう。

　↓実際のログ
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/968337/30554e60-5cbe-1f86-d730-290b61620c77.png' width='500px;'>
このrenderのせいで、たった25件の投稿表示に1800ms以上の時間がかかってしまう。しかも、Ajaxによる非同期処理でいいねボタンの切り替えを実装しているため(DRYの観点でも)、いいねボタンをパーシャル化しなければならない。(他に方法があれば知りたいです！) 同じ状況の方も多いのでは？


# 解決策
renderの**layoutオプション**と**yieldメソッド**を使うことで解決した。

# ER図
![Screen Shot 2021-10-16 at 23.42.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/968337/2074107b-9ede-e0c2-e1b1-779812bbef3f.png)


# [NGパターン] renderの繰り返しが発生するrenderの使い方

#### コントローラー

```ruby:posts_controller.rb
def index
  @posts = Post.All        # 今回はN+１問題などは考慮しません
end
```

#### View

```haml:views/posts/index.html
%div
  = render partial: 'posts/shared/post', collection: @posts, as: :post

```

```haml:views/posts/shared/_post.html
%div
  = post.id
  = post.content
  .post-likes{ id: "post-#{post.id}-likes" }
      = render 'likes/like', post: post
```

```haml:views/likes/_like.html
- if post.already_liked?(current_user) # 「いいね」済みならtrueを返すメソッド
  = link_to post_like_path(post, post.likes), mehod: :delete, remote: true do
    %font
      .fas.fa-heart.likes-heart-already
- else
  = link_to post_likes_path(post), method: :post, remote: true do
    %font
      .far.fa-heart
%span{ id: "post-#{post.id}-likes-count" }
  = post.likes.count
```
#### ログ

```terminal:terminal
web_1              |   ↳ app/controllers/application_controller.rb:36
web_1              |   Rendering posts/index.html.haml within layouts/application
web_1              |   Rendered likes/_like.html.haml (7.7ms)
web_1              |   Rendered likes/_like.html.haml (0.6ms)
web_1              |   Rendered likes/_like.html.haml (0.6ms)
web_1              |   Rendered likes/_like.html.haml (0.5ms)
　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　〜省略〜
web_1              |   Rendered likes/_like.html.haml (0.6ms)
web_1              |   Rendered likes/_like.html.haml (0.4ms)
web_1              |   Rendered likes/_like.html.haml (0.4ms)
web_1              |   Rendered collection of posts/shared/_post.html.haml [25 times] (663.9ms)
web_1              |   Rendered posts/index.html.haml within layouts/application (690.5ms)
web_1              | Completed 200 OK in 1847ms (Views: 1791.0ms | ActiveRecord: 3.4ms)
```

# [OKパターン]　renderの繰り返しが発生しないrenderの使い方
#### コントローラー
```ruby:posts_controller.rb
# コントローラは改善前と同じです。

def index
  @posts = Post.All        # 今回はN+１問題などは考慮しません
end
```

#### View
↓ renderメソッドのlayoutオプションを使い、パーシャルに_like.htmlを指定。さらに、collectionにはpostsを指定する。

```haml:views/posts/index.html
.posts-wrap
  %div{ id: 'post-items' }
    = render partial: 'likes/like', layout: 'posts/post_layout', collection: @posts, as: :post

/ _post_layoutパーシャルは新たに作成します。
```

↓ yieldメソッドにて_like.htmlパーシャルを表示する。

```haml:views/posts/post_layout.html
%div{ id: "post-#{post.id}" }
  %div
    = post.id
    = post.content
    .post-likes{ id: "post-#{post.id}-likes" }
      = yield

/ このファイルはは新たに作成しています。
```

#### ログ

```terminal:terminal
web_1              |   ↳ app/controllers/application_controller.rb:36
web_1              |   Rendering posts/index.html.haml within layouts/application
web_1              |   Rendered collection of likes/_like.html.haml [25 times] (33.6ms)
web_1              |   Rendered posts/index.html.haml within layouts/application (61.2ms)
web_1              | Completed 200 OK in 343ms (Views: 295.7ms | ActiveRecord: 2.9ms)
```

**1847ms**も要していたアクセス時間を**343ms**まで短縮することができました！(正確には短縮ではないですが)

# おわり
なんとなく理解した気でいたrenderメソッドとyieldメソッドについて理解を深めることができました。
解決までに時間がかかってしまったので、解決できた時は本当に感動しました。

初めての投稿で、わかりづらい点があるかもしれません。
意見、アドバイス等よろしくお願いします！

今後も学びのアウトプットをしていきたいと思います。

# 参考にした記事

https://qiita.com/hayashino/items/c2a4e7d3edbdcce3cd2a

https://qiita.com/otakumesi/items/74a75cd6ebff7eba5c63

https://railsdoc.com/page/render