Skip to content

Commit 92f27de

Browse files
committed
fix(ViewHelper) use controller lifecycle hooks
1 parent 2a98a4d commit 92f27de

10 files changed

+64
-56
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#### Bug Fixes
1010

11+
- Use controller lifecycle hooks for view helper (tests don't run middlewares)
12+
1113
## 1.3.0 (September 15, 2015)
1214

1315
#### Breaking Changes

README.md

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ rails g react:install
3434
```
3535

3636
This will:
37-
- create a `components.js` manifest file and a `app/assets/javascripts/components/` directory,
37+
- create a `components.js` manifest file and a `app/assets/javascripts/components/` directory,
3838
where you will put your components
3939
- place the following in your `application.js`:
4040

@@ -48,7 +48,7 @@ where you will put your components
4848

4949
### React.js builds
5050

51-
You can pick which React.js build (development, production, with or without [add-ons]((http://facebook.github.io/react/docs/addons.html)))
51+
You can pick which React.js build (development, production, with or without [add-ons]((http://facebook.github.io/react/docs/addons.html)))
5252
to serve in each environment by adding a config. Here are the defaults:
5353

5454
```ruby
@@ -71,10 +71,10 @@ MyApp::Application.configure do
7171
end
7272
```
7373

74-
After restarting your Rails server, `//= require react` will provide the build of React.js which
74+
After restarting your Rails server, `//= require react` will provide the build of React.js which
7575
was specified by the configurations.
7676

77-
`react-rails` offers a few other options for versions & builds of React.js.
77+
`react-rails` offers a few other options for versions & builds of React.js.
7878
See [VERSIONS.md](https://github.com/reactjs/react-rails/blob/master/VERSIONS.md) for more info about
7979
using the `react-source` gem or dropping in your own copies of React.js.
8080

@@ -121,7 +121,7 @@ config.react.jsx_transform_options = {
121121

122122
### Rendering & mounting
123123

124-
`react-rails` includes a view helper (`react_component`) and an unobtrusive JavaScript driver (`react_ujs`)
124+
`react-rails` includes a view helper (`react_component`) and an unobtrusive JavaScript driver (`react_ujs`)
125125
which work together to put React components on the page. You should require the UJS driver
126126
in your manifest after `react` (and after `turbolinks` if you use [Turbolinks](https://github.com/rails/turbolinks)).
127127

@@ -133,7 +133,7 @@ The __view helper__ puts a `div` on the page with the requested component class
133133
<div data-react-class="HelloMessage" data-react-props="{&quot;name&quot;:&quot;John&quot;}"></div>
134134
```
135135

136-
On page load, the __`react_ujs` driver__ will scan the page and mount components using `data-react-class`
136+
On page load, the __`react_ujs` driver__ will scan the page and mount components using `data-react-class`
137137
and `data-react-props`.
138138

139139
If Turbolinks is present components are mounted on the `page:change` event, and unmounted on `page:before-unload`.
@@ -169,9 +169,9 @@ _(It will be also be mounted by the UJS on page load.)_
169169

170170
There are some requirements for this to work:
171171

172-
- `react-rails` must load your code. By convention it uses `components.js`, which was created
172+
- `react-rails` must load your code. By convention it uses `components.js`, which was created
173173
by the install task. This file must include your components _and_ their dependencies (eg, Underscore.js).
174-
- Your components must be accessible in the global scope.
174+
- Your components must be accessible in the global scope.
175175
If you are using `.js.jsx.coffee` files then the wrapper function needs to be taken into account:
176176

177177
```coffee
@@ -180,7 +180,7 @@ If you are using `.js.jsx.coffee` files then the wrapper function needs to be ta
180180
render: ->
181181
`<ExampleComponent videos={this.props.videos} />`
182182
```
183-
- Your code can't reference `document`. Prerender processes don't have access to `document`,
183+
- Your code can't reference `document`. Prerender processes don't have access to `document`,
184184
so jQuery and some other libs won't work in this environment :(
185185

186186
You can configure your pool of JS virtual machines and specify where it should load code:
@@ -222,10 +222,10 @@ By default, your current layout will be used and the component, rather than a vi
222222

223223
### Component generator
224224

225-
`react-rails` ships with a Rails generator to help you get started with a simple component scaffold.
226-
You can run it using `rails generate react:component ComponentName (--es6)`.
227-
The generator takes an optional list of arguments for default propTypes,
228-
which follow the conventions set in the [Reusable Components](http://facebook.github.io/react/docs/reusable-components.html)
225+
`react-rails` ships with a Rails generator to help you get started with a simple component scaffold.
226+
You can run it using `rails generate react:component ComponentName (--es6)`.
227+
The generator takes an optional list of arguments for default propTypes,
228+
which follow the conventions set in the [Reusable Components](http://facebook.github.io/react/docs/reusable-components.html)
229229
section of the React documentation.
230230

231231
For example:
@@ -294,7 +294,7 @@ Note that the arguments for `oneOf` and `oneOfType` must be enclosed in single q
294294

295295
### Jbuilder & react-rails
296296

297-
If you use Jbuilder to pass a JSON string to `react_component`, make sure your JSON is a stringified hash,
297+
If you use Jbuilder to pass a JSON string to `react_component`, make sure your JSON is a stringified hash,
298298
not an array. This is not the Rails default -- you should add the root node yourself. For example:
299299

300300
```ruby
@@ -313,7 +313,7 @@ end
313313

314314
## CoffeeScript
315315

316-
It is possible to use JSX with CoffeeScript. To use CoffeeScript, create files with an extension `.js.jsx.coffee`.
316+
It is possible to use JSX with CoffeeScript. To use CoffeeScript, create files with an extension `.js.jsx.coffee`.
317317
We also need to embed JSX code inside backticks so that CoffeeScript ignores the syntax it doesn't understand.
318318
Here's an example:
319319

@@ -348,8 +348,8 @@ Any subclass of `ExecJSRenderer` may use those hooks (for example, `SprocketsRen
348348
`react-rails` uses a "helper implementation" class to generate the output of the `react_component` helper. The helper is initialized once per request and used for each `react_component` call during that request. You can provide a custom helper class to `config.react.view_helper_implementation`. The class must implement:
349349

350350
- `#react_component(name, props = {}, options = {}, &block)` to return a string to inject into the Rails view
351-
- `#setup(rack_env)`, called when the helper is initialized at the start of the request
352-
- `#teardown(rack_env)`, called at the end of the request
351+
- `#setup(controller_instance)`, called when the helper is initialized at the start of the request
352+
- `#teardown(controller_instance)`, called at the end of the request
353353

354354
`react-rails` provides one implementation, `React::Rails::ComponentMount`.
355355

lib/react/rails.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
require 'react/rails/asset_variant'
22
require 'react/rails/engine'
33
require 'react/rails/railtie'
4-
require 'react/rails/render_middleware'
4+
require 'react/rails/controller_lifecycle'
55
require 'react/rails/version'
66
require 'react/rails/component_mount'
77
require 'react/rails/view_helper'

lib/react/rails/component_mount.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class ComponentMount
1010
include ActionView::Helpers::TextHelper
1111
attr_accessor :output_buffer
1212

13-
# RenderMiddleware calls these hooks
13+
# ControllerLifecycle calls these hooks
1414
# You can use them in custom helper implementations
1515
def setup(env)
1616
end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module React
2+
module Rails
3+
module ControllerLifecycle
4+
extend ActiveSupport::Concern
5+
6+
included do
7+
before_action :setup_react_component_helper
8+
after_action :teardown_react_component_helper
9+
end
10+
11+
def setup_react_component_helper
12+
new_helper = React::Rails::ViewHelper.helper_implementation_class.new
13+
new_helper.setup(self)
14+
@__react_component_helper = new_helper
15+
end
16+
17+
def teardown_react_component_helper
18+
@__react_component_helper.teardown(self)
19+
end
20+
end
21+
end
22+
end

lib/react/rails/railtie.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,18 @@ class Railtie < ::Rails::Railtie
2424

2525
# Include the react-rails view helper lazily
2626
initializer "react_rails.setup_view_helpers", group: :all do |app|
27-
app.config.middleware.use(::React::Rails::RenderMiddleware)
27+
2828
app.config.react.jsx_transformer_class ||= React::JSX::DEFAULT_TRANSFORMER
2929
React::JSX.transformer_class = app.config.react.jsx_transformer_class
3030
React::JSX.transform_options = app.config.react.jsx_transform_options
3131

3232
app.config.react.view_helper_implementation ||= React::Rails::ComponentMount
3333
React::Rails::ViewHelper.helper_implementation_class = app.config.react.view_helper_implementation
34+
35+
ActiveSupport.on_load(:action_controller) do
36+
include ::React::Rails::ControllerLifecycle
37+
end
38+
3439
ActiveSupport.on_load(:action_view) do
3540
include ::React::Rails::ViewHelper
3641
end

lib/react/rails/render_middleware.rb

Lines changed: 0 additions & 19 deletions
This file was deleted.

lib/react/rails/view_helper.rb

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@ module Rails
33
module ViewHelper
44
# This class will be used for inserting tags into HTML.
55
# It should implement:
6-
# - #setup(env)
7-
# - #teardown(env)
6+
# - #setup(controller_instance)
7+
# - #teardown(controller_instance)
88
# - #react_component(name, props, options &block)
99
# The default is {React::Rails::ComponentMount}
1010
mattr_accessor :helper_implementation_class
1111

1212
def react_component(*args, &block)
13-
impl_key = React::Rails::RenderMiddleware::HELPER_IMPLEMENTATION_KEY
14-
helper_obj = request.env[impl_key]
13+
helper_obj = @__react_component_helper
1514
helper_obj.react_component(*args, &block)
1615
end
1716
end

test/react/rails/render_middleware_test.rb renamed to test/react/rails/controller_lifecycle_test.rb

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
# calls to `react_component`
55
class DummyHelperImplementation
66
attr_reader :events
7+
78
def initialize
89
@events = []
910
end
1011

11-
def setup(env)
12-
@events << :setup
12+
def setup(controller)
13+
@events << (controller.params["param_test"] || :setup)
1314
end
1415

1516
def teardown(env)
@@ -21,9 +22,7 @@ def react_component(*args)
2122
end
2223
end
2324

24-
class RenderMiddlewareTest < ActionDispatch::IntegrationTest
25-
impl_key = React::Rails::RenderMiddleware::HELPER_IMPLEMENTATION_KEY
26-
25+
class ControllerLifecycleTest < ActionDispatch::IntegrationTest
2726
def setup
2827
@previous_helper_implementation = React::Rails::ViewHelper.helper_implementation_class
2928
React::Rails::ViewHelper.helper_implementation_class = DummyHelperImplementation
@@ -35,22 +34,22 @@ def teardown
3534

3635
test "it creates a helper object and puts it in the request env" do
3736
get '/pages/1'
38-
helper_obj = request.env[impl_key]
37+
helper_obj = controller.instance_variable_get(:@__react_component_helper)
3938
assert(helper_obj.is_a?(DummyHelperImplementation), "It uses the view helper implementation class")
4039
end
4140

4241
test "it calls setup and teardown methods" do
43-
get '/pages/1'
44-
helper_obj = request.env[impl_key]
45-
lifecycle_steps = [:setup, :react_component, :teardown]
42+
get '/pages/1?param_test=123'
43+
helper_obj = controller.instance_variable_get(:@__react_component_helper)
44+
lifecycle_steps = ["123", :react_component, :teardown]
4645
assert_equal(lifecycle_steps, helper_obj.events)
4746
end
4847

4948
test "there's a new helper object for every request" do
5049
get '/pages/1'
51-
first_helper = request.env[impl_key]
50+
first_helper = controller.instance_variable_get(:@__react_component_helper)
5251
get '/pages/1'
53-
second_helper = request.env[impl_key]
52+
second_helper = controller.instance_variable_get(:@__react_component_helper)
5453
assert(first_helper != second_helper, "The helper for the second request is brand new")
5554
end
5655
end
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
require 'test_helper'
22

33
class PagesControllerTest < ActionController::TestCase
4-
test 'should not raise react_component undefined in ActionController::TestCase' do
4+
test 'renders successfully' do
55
get :show, id: 1
6-
assert_not_raised ActionView::Template::Error
6+
assert_equal(200, response.status)
77
end
88
end

0 commit comments

Comments
 (0)