Skip to content

Commit 38751a2

Browse files
authored
Merge pull request #277 from inertiajs/fix-for-shared-scroll-and-deferred-props
Fix for shared scroll and deferred props
2 parents 99774c8 + 0f4c032 commit 38751a2

File tree

5 files changed

+142
-36
lines changed

5 files changed

+142
-36
lines changed

lib/inertia_rails/renderer.rb

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66

77
module InertiaRails
88
class Renderer
9-
attr_reader(
10-
:component,
11-
:configuration,
12-
:controller,
13-
:props,
14-
:view_data,
15-
:encrypt_history,
16-
:clear_history
17-
)
9+
%i[component configuration controller props view_data encrypt_history
10+
clear_history].each do |method_name|
11+
define_method(method_name) do
12+
InertiaRails.deprecator.warn(
13+
"[DEPRECATION] Accessing `InertiaRails::Renderer##{method_name}` is deprecated and will be removed in v4.0"
14+
)
15+
instance_variable_get("@#{method_name}")
16+
end
17+
end
1818

1919
def initialize(component, controller, request, response, render_method, **options)
2020
if component.is_a?(Hash) && options.key?(:props)
@@ -24,15 +24,20 @@ def initialize(component, controller, request, response, render_method, **option
2424

2525
@controller = controller
2626
@configuration = controller.__send__(:inertia_configuration)
27-
@component = resolve_component(component)
2827
@request = request
2928
@response = response
3029
@render_method = render_method
31-
@props = options.fetch(:props, component.is_a?(Hash) ? component : controller.__send__(:inertia_view_assigns))
3230
@view_data = options.fetch(:view_data, {})
33-
@deep_merge = options.fetch(:deep_merge, configuration.deep_merge_shared_data)
34-
@encrypt_history = options.fetch(:encrypt_history, configuration.encrypt_history)
31+
@encrypt_history = options.fetch(:encrypt_history, @configuration.encrypt_history)
3532
@clear_history = options.fetch(:clear_history, controller.session[:inertia_clear_history] || false)
33+
34+
deep_merge = options.fetch(:deep_merge, @configuration.deep_merge_shared_data)
35+
passed_props = options.fetch(:props,
36+
component.is_a?(Hash) ? component : @controller.__send__(:inertia_view_assigns))
37+
@props = merge_props(shared_data, passed_props, deep_merge)
38+
39+
@component = resolve_component(component)
40+
3641
@controller.instance_variable_set('@_inertia_rendering', true)
3742
controller.inertia_meta.add(options[:meta]) if options[:meta]
3843
end
@@ -48,41 +53,41 @@ def render
4853
@render_method.call json: page.to_json, status: @response.status, content_type: Mime[:json]
4954
else
5055
begin
51-
return render_ssr if configuration.ssr_enabled
56+
return render_ssr if @configuration.ssr_enabled
5257
rescue StandardError
5358
nil
5459
end
55-
controller.instance_variable_set('@_inertia_page', page)
56-
@render_method.call template: 'inertia', layout: layout, locals: view_data.merge(page: page)
60+
@controller.instance_variable_set('@_inertia_page', page)
61+
@render_method.call template: 'inertia', layout: layout, locals: @view_data.merge(page: page)
5762
end
5863
end
5964

6065
private
6166

6267
def render_ssr
63-
uri = URI("#{configuration.ssr_url}/render")
68+
uri = URI("#{@configuration.ssr_url}/render")
6469
res = JSON.parse(Net::HTTP.post(uri, page.to_json, 'Content-Type' => 'application/json').body)
6570

66-
controller.instance_variable_set('@_inertia_ssr_head', res['head'].join.html_safe)
67-
@render_method.call html: res['body'].html_safe, layout: layout, locals: view_data.merge(page: page)
71+
@controller.instance_variable_set('@_inertia_ssr_head', res['head'].join.html_safe)
72+
@render_method.call html: res['body'].html_safe, layout: layout, locals: @view_data.merge(page: page)
6873
end
6974

7075
def layout
71-
layout = configuration.layout
76+
layout = @configuration.layout
7277
layout.nil? || layout
7378
end
7479

7580
def shared_data
76-
controller.__send__(:inertia_shared_data)
81+
@controller.__send__(:inertia_shared_data)
7782
end
7883

7984
# Cast props to symbol keyed hash before merging so that we have a consistent data structure and
8085
# avoid duplicate keys after merging.
8186
#
8287
# Functionally, this permits using either string or symbol keys in the controller. Since the results
8388
# is cast to json, we should treat string/symbol keys as identical.
84-
def merge_props(shared_props, props)
85-
if @deep_merge
89+
def merge_props(shared_props, props, deep_merge)
90+
if deep_merge
8691
shared_props.deep_symbolize_keys.deep_merge!(props.deep_symbolize_keys)
8792
else
8893
shared_props.symbolize_keys.merge(props.symbolize_keys)
@@ -91,16 +96,15 @@ def merge_props(shared_props, props)
9196

9297
def computed_props
9398
# rubocop:disable Style/MultilineBlockChain
94-
merge_props(shared_data, props)
95-
.then do |merged_props| # Always keep errors in the props
99+
@props
100+
.tap do |merged_props| # Always keep errors in the props
96101
if merged_props.key?(:errors) && !merged_props[:errors].is_a?(BaseProp)
97102
errors = merged_props[:errors]
98103
merged_props[:errors] = InertiaRails.always { errors }
99104
end
100-
merged_props
101105
end
102106
.then { |props| deep_transform_props(props) } # Internal hydration/filtering
103-
.then { |props| configuration.prop_transformer(props: props) } # Apply user-defined prop transformer
107+
.then { |props| @configuration.prop_transformer(props: props) } # Apply user-defined prop transformer
104108
.tap do |props| # Add meta tags last (never transformed)
105109
props[:_inertia_meta] = meta_tags if meta_tags.present?
106110
end
@@ -111,12 +115,12 @@ def page
111115
return @page if defined?(@page)
112116

113117
@page = {
114-
component: component,
118+
component: @component,
115119
props: computed_props,
116120
url: @request.original_fullpath,
117-
version: configuration.version,
118-
encryptHistory: encrypt_history,
119-
clearHistory: clear_history,
121+
version: @configuration.version,
122+
encryptHistory: @encrypt_history,
123+
clearHistory: @clear_history,
120124
}
121125

122126
deferred_props = deferred_props_keys
@@ -138,9 +142,9 @@ def deep_transform_props(props, parent_path = [])
138142
transformed_props[key] =
139143
case prop
140144
when BaseProp
141-
prop.call(controller)
145+
prop.call(@controller)
142146
when Proc
143-
controller.instance_exec(&prop)
147+
@controller.instance_exec(&prop)
144148
else
145149
prop
146150
end
@@ -248,12 +252,12 @@ def partial_except_keys
248252
end
249253

250254
def rendering_partial_component?
251-
@request.headers['X-Inertia-Partial-Component'] == component
255+
@request.headers['X-Inertia-Partial-Component'] == @component
252256
end
253257

254258
def resolve_component(component)
255259
if component == true || component.is_a?(Hash)
256-
configuration.component_path_resolver(path: controller.controller_path, action: controller.action_name)
260+
@configuration.component_path_resolver(path: @controller.controller_path, action: @controller.action_name)
257261
else
258262
component
259263
end
@@ -289,7 +293,7 @@ def excluded_by_except_partial_keys?(path_with_prefixes)
289293
end
290294

291295
def meta_tags
292-
controller.inertia_meta.meta_tags
296+
@controller.inertia_meta.meta_tags
293297
end
294298
end
295299
end

spec/dummy/app/controllers/inertia_render_test_controller.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,17 @@ def deferred_props
129129
}
130130
end
131131

132+
inertia_share only: [:shared_deferred_props] do
133+
{
134+
grit: InertiaRails.defer { 'intense' },
135+
}
136+
end
137+
def shared_deferred_props
138+
render inertia: 'TestComponent', props: {
139+
name: 'Brian',
140+
}
141+
end
142+
132143
def scroll_test
133144
pagy = (defined?(Pagy::Offset) ? Pagy::Offset : Pagy).new(
134145
next: 2,
@@ -141,6 +152,21 @@ def scroll_test
141152
}
142153
end
143154

155+
inertia_share only: [:shared_scroll_test] do
156+
pagy = (defined?(Pagy::Offset) ? Pagy::Offset : Pagy).new(
157+
next: 2,
158+
page: 1,
159+
count: 100
160+
)
161+
{
162+
users: InertiaRails.scroll(pagy) { [{ id: 1, name: 'User 1' }, { id: 2, name: 'User 2' }] },
163+
}
164+
end
165+
166+
def shared_scroll_test
167+
render inertia: 'TestComponent'
168+
end
169+
144170
def prepend_merge_test
145171
render inertia: 'TestComponent', props: {
146172
prepend_prop: InertiaRails.merge(prepend: true) { %w[item1 item2] },

spec/dummy/config/routes.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@
3838
get 'except_props' => 'inertia_render_test#except_props'
3939
get 'merge_props' => 'inertia_render_test#merge_props'
4040
get 'deferred_props' => 'inertia_render_test#deferred_props'
41+
get 'shared_deferred_props' => 'inertia_render_test#shared_deferred_props'
4142
get 'scroll_test' => 'inertia_render_test#scroll_test'
43+
get 'shared_scroll_test' => 'inertia_render_test#shared_scroll_test'
4244
get 'prepend_merge_test' => 'inertia_render_test#prepend_merge_test'
4345
get 'nested_paths_test' => 'inertia_render_test#nested_paths_test'
4446
get 'reset_test' => 'inertia_render_test#reset_test'

spec/inertia/renderer_spec.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe InertiaRails::Renderer do
4+
let(:deprecator) do
5+
double(warn: nil).tap do |deprecator|
6+
allow(InertiaRails).to receive(:deprecator).and_return(deprecator)
7+
end
8+
end
9+
10+
%i[component configuration controller props view_data encrypt_history clear_history].each do |method_name|
11+
it "has a deprecated #{method_name} accessor" do
12+
configuration = double('configuration',
13+
encrypt_history: true,
14+
deep_merge_shared_data: false,
15+
clear_history: false)
16+
17+
controller = double('controller',
18+
inertia_configuration: configuration,
19+
inertia_view_assigns: {},
20+
session: {},
21+
inertia_shared_data: {})
22+
23+
request = double('request', headers: {})
24+
response = double('response', headers: {}, set_header: nil)
25+
render_method = ->(args) {}
26+
27+
renderer = InertiaRails::Renderer.new('MyComponent', controller, request, response, render_method)
28+
29+
expect(deprecator).to receive(:warn)
30+
.with(
31+
"[DEPRECATION] Accessing `InertiaRails::Renderer##{method_name}` is deprecated and will be removed in v4.0"
32+
)
33+
34+
renderer.send(method_name)
35+
end
36+
end
37+
end

spec/inertia/rendering_spec.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,27 @@
661661
end
662662
end
663663

664+
context 'shared scroll props rendering' do
665+
let(:headers) { { 'X-Inertia' => true } }
666+
667+
before do
668+
# Create a mock controller action that returns scroll props
669+
get '/shared_scroll_test', headers: headers
670+
end
671+
672+
it 'includes scroll props metadata in response without reset' do
673+
expect(response).to be_successful
674+
expect(response.parsed_body['scrollProps']).to include('users')
675+
expect(response.parsed_body['scrollProps']['users']).to include(
676+
'pageName' => 'page',
677+
'currentPage' => 1,
678+
'nextPage' => 2,
679+
'previousPage' => nil,
680+
'reset' => false
681+
)
682+
end
683+
end
684+
664685
context 'prepend merge props rendering' do
665686
let(:headers) { { 'X-Inertia' => true } }
666687

@@ -759,6 +780,22 @@
759780
expect(response.parsed_body['deferredProps']).to eq(nil)
760781
end
761782
end
783+
784+
context 'with shared data' do
785+
let(:headers) { { 'X-Inertia' => true } }
786+
787+
before { get shared_deferred_props_path, headers: headers }
788+
789+
it 'does not include defer props inside props in first load' do
790+
expect(response.parsed_body['props']).to eq({ 'name' => 'Brian' })
791+
end
792+
793+
it 'returns deferredProps' do
794+
expect(response.parsed_body['deferredProps']).to eq(
795+
'default' => ['grit']
796+
)
797+
end
798+
end
762799
end
763800
end
764801

0 commit comments

Comments
 (0)