Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Consistently evaluate passed in block content (#1407)
* Consistently evaluate passed in block content Currently slots aren't composable due to the lifecycle of components, where we attempt to avoid evaluating the block passed to `render_in` (aka `content` in the library). This creates an inconsistent experience where delegation works only if `content` was already evaluated. The following code looks correct, but the `c.title` code will never be executed: ```ruby class MyComponent delegate :title, to: :@Sidebar def initialize @Sidebar = SidebarComponent.new end def call content_tag :div do render @Sidebar end end end ``` ```erb <%= render MyComponent.new(:title) do |c| %> <%= c.title("My sidebar") %> <% end %> ``` The block passed that includes the call to `c.title("My sidebar")` is never evaluated. This is a side-effect of a performance optimization where we attempt to lazily evaluate the block passed to `render_in`. This was primarily evaluating the entire render chain for components return `false` when `render?` is called since it's unnecessary work. If the component were slightly different and called `content` for any reason the above code would work as expected: ```ruby class MyComponent delegate :title, to: :@Sidebar def initialize @Sidebar = SidebarComponent.new end def call # this causes `content` to be evaluated, resulting in # <%= c.title(title: "My sidebar") %> being called. Now this # component will work as expected. modified_content = content.replace('foo', 'bar') content_tag :div do render @Sidebar end end end ``` This is an example of the optimization causing inconsistent behavior in the library. The change above causes `content` to be called before `#call`, so any setup/mutations/side-effects are now guaranteed to be reflected in the component output. Here's what the change in the component lifecycle looks like: ``` \#before_render -> #render? -> #call (#content can be called anywhere in the above chain, or never called) ``` Now the lifecycle looks like: ``` \#before_render -> #render? -> #content -> #call (#content isn't used, it's just evaluated to ensure side-effects are performed) ``` * Remove title kwarg * Add changelog entry * Add caveat to changelog entry Co-authored-by: Joel Hawksley <joel@hawksley.org>
- Loading branch information