diff --git a/docs/guide/lifecycle.md b/docs/guide/lifecycle.md index de786d366..fbabc3d97 100644 --- a/docs/guide/lifecycle.md +++ b/docs/guide/lifecycle.md @@ -13,6 +13,8 @@ Since 2.8.0 Define a `before_render` method to be called before a component is rendered, when `helpers` is able to be used: +`before_render` is only called if `render?` returns true. Since `render?` runs first, it must not depend on state set in `before_render`. + ```ruby # app/components/example_component.rb class ExampleComponent < ViewComponent::Base diff --git a/lib/view_component/base.rb b/lib/view_component/base.rb index b525c7f5a..34585f4ae 100644 --- a/lib/view_component/base.rb +++ b/lib/view_component/base.rb @@ -139,9 +139,9 @@ def render_in(view_context, **, &block) @__vc_render_in_block = block @view_context.instance_variable_set(:@virtual_path, virtual_path) - before_render - if render? + before_render + value = nil @output_buffer.with_buffer do @@ -239,6 +239,7 @@ def output_postamble # Called before rendering the component. Override to perform operations that # depend on having access to the view context, such as helpers. + # Only called if `render?` returns true. # # @return [void] def before_render diff --git a/test/sandbox/test/rendering_test.rb b/test/sandbox/test/rendering_test.rb index 6e53e8749..dd60fe7ce 100644 --- a/test/sandbox/test/rendering_test.rb +++ b/test/sandbox/test/rendering_test.rb @@ -423,6 +423,33 @@ def test_conditional_rendering assert_no_text("component was rendered") end + def test_before_render_is_not_called_when_render_is_false + component = ConditionalRenderComponent.new(should_render: false) + callbacks = [] + component.define_singleton_method(:before_render) { callbacks << :before_render } + + render_inline(component) + + assert_empty callbacks + refute_component_rendered + assert_predicate rendered_content, :html_safe? + end + + def test_before_render_is_called_after_render_predicate + component = ConditionalRenderComponent.new(should_render: true) + callbacks = [] + component.define_singleton_method(:render?) do + callbacks << :render? + super() + end + component.define_singleton_method(:before_render) { callbacks << :before_render } + + render_inline(component) + + assert_equal [:render?, :before_render], callbacks + assert_text("component was rendered") + end + def test_conditional_rendering_if_content_provided render_inline(ConditionalContentComponent.new)