New year, new codebase for a side-project of mine, and I decided to go with ReactOnRails, turbolinks and other sorcery that will help me go faster but won’t sacrifice code quality.
An issue I found with this approach is that it wasn’t clear to me how to render a React Component as a layout, since the yield
call in my application.html.erb
would render the <%= react_component ... %>
part in the view, as described in the ReactOnRails README file. Here’s the way I found in order to do so:
Added a react_layout method in my controller. That way I can overload this method in child classes
# file app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def react_layout
'LayoutDefault'
end
def react_layout_props
{ user: { id: current_user.id } } # ... fill in your layout props
end
end
Created a ReactHelper
module with a react_view
wrapper method, which utilizes react_component
internally.
# file app/helpers/react_helper.rb
module ReactHelper
def react_view viewname, props: {}, layout: nil, layout_props: {}
layout ||= controller.react_layout
layout_component_props = controller.react_layout_props.merge(layout_props)
react_component(layout, props: layout_component_props.merge({
component: viewname,
componentProps: props,
}))
end
end
Replaced calls to react_component
with react_view
, for example
# file app/views/dashboard/index.html.erb
<%= react_view("DashboardView", props: @props) %>
Dynamically rendered the component requested in the view inside LayoutDefault.jsx
// store this in a location resolvable by webpacker
import React from 'react';
import PropTypes from 'prop-types';
const LayoutDefault = ({ component, componentProps }) => {
const ViewComponent = ReactOnRails.getComponent(component);
return (
<React.Fragment>
... Layout area ...
<ViewComponent.component {...componentProps} />
... Layout area ...
</React.Fragment>
);
};
LayoutDefault.propTypes = {
component: PropTypes.string.isRequired,
componentProps: PropTypes.object,
};
LayoutDefault.defaultProps = {
componentProps: {},
};
export default LayoutDefault;
And done. The app now renders the react layout and the component I’ve requested in my view inside.
Pro tip: You need to declare ReactOnRails
as a global in your eslint config if you’re using a linter (which you should)