Skip to content

Instantly share code, notes, and snippets.

@vuongpdz
Last active October 9, 2020 02:24
Show Gist options
  • Save vuongpdz/14675cd20f82b2ca448ee2fba406171f to your computer and use it in GitHub Desktop.
Save vuongpdz/14675cd20f82b2ca448ee2fba406171f to your computer and use it in GitHub Desktop.
Report on view_component Gem & component architecture in Rails

REPORT VIEW_COMPONENT

About Github view_component gem (https://github.com/github/view_component) (Here I don't mention component architecture but write directly about view_component gem because for each library, the component architecture will be implemented differently at some point)

Advantages: (See more here: https://github.com/github/view_component#why-should-i-use-components)

(1) Encourage separating logic from views and reside them in a ruby class

(2) Removed the ability to use an instance variable (@) in partial, view_component requires passing variables from outside through new) => Finding the origin of the variable is easier

(3) view_component ~ 10 times faster than partial (https://github.com/github/view_component/blob/master/performance/benchmark.rb)

(4) Easily reuse view_component template by inherit from another component

(5) Unit-test view_component fast + easier than partial

(6) view_component encourages coupling CSS & JS with the component

Although they are advantages, if analyzed carefully, not every point is sounds:

  • For (1) and (2), although view_component limits the disadvantages of helpers & partials. However, there are ways to repeat the old mistakes. For example:
# controllers/posts_controller.rb
class PostsController < ApplicationController
  attr_accessor :var_1

  def show
    @post = Post.find(params[:id])
    render(PostShowComponent.new(post: @post))
  end

  def index
    @posts = Post.all
    render(PostIndexComponent.new(post: @post))
  end
end

# component/base_component.rb
class BaseComponent < ViewComponent::Base
  delegate :var_1, to: :controller
end

# components/post_index_component.rb
class PostIndexComponent < BaseComponent

end

# components/post_index_component.html.slim
= var_1 = "haha, I changed it even if others might need it not to be changed"
= "I'm trying to reuse #{var_1.to_s} even if I might be out of my original context"
  • In the above example, we can see that since we can access controller when subclass ViewComponent::Base, we can access var_1 - created by the controller. So if the controller is having problems with too many places that can set var_1, it can still lead to subtle & hard to detect bugs when rendering post_index_component in a different context from the original one. To handle this bad practice, we need conventions for our source code and view_component can't help in this case. By using convention, we can handle this bad practice even if we use partial, for example:

For partials:

  • Only use the variable from local_assigns to render the interface
  • Do not use ruby to set variables out of the partial scope

For view_component:

  • Only use variables passed in initialize to render interfaces
  • Do not set variables outside the scope of the component

In short, in my opinion, (1) and (2) - the advantage of data flow mentioned in https://github.com/github/view_component#data-flow should not be considered the advantage of view_component over partial

Furthermore, the component architecture encourages the splitting of the view into multiple components for reuse, extension + application of the design system. So, if we simply convert partial to a component with a corresponding name, I think we have missed a very important part of the component architecture. See how GitHub builds view_component (https://github.com/primer/view_components) for their Primer Design System similar to Primer React (https://github.com/primer/components)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment