rails new reactrails6 --webpack=react --skip-coffee --skip-sprockets
cd reactrails6
bundle add react-rails
yarn add react_ujs reactstrap bootstrap
yarn check
bin/rails generate react:install
- Edit the
config/initializers/content_security_policy.rb
file and uncomment the line that starts withpolicy.connect_src
Our little sample program will setup a simple form and resource for accepting charity donations.
bin/rails g scaffold Donations charity:string amount:float
- Run
webpacker-dev-server
in a separate terminal session. bin/rails db:migrate
We will now customize our little app with a navigation bar and some form elements.
-
Setup a root route in
config/routes.rb
root 'donations#index'
-
We will put all our customer components in app/javascript/components
-
Create a
SampleNavBar.jsx
component to added to the application layout.import React from "react" import PropTypes from "prop-types" import { Collapse, Navbar, NavbarToggler, NavbarBrand, Nav, NavItem, NavLink, UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; class SampleNavBar extends React.Component { constructor(props) { super(props); this.toggle = this.toggle.bind(this); this.state = { isOpen: false }; } toggle() { this.setState({ isOpen: !this.state.isOpen }); } render () { return ( <React.Fragment> <Navbar color="dark" dark expand="md"> <NavbarBrand color="white" href="/">reactstrap</NavbarBrand> <NavbarToggler onClick={this.toggle} /> <Collapse isOpen={this.state.isOpen} navbar> <Nav className="ml-auto" navbar> <NavItem> <NavLink href="/components/">Components</NavLink> </NavItem> <NavItem> <NavLink href="https://github.com/reactstrap/reactstrap">GitHub</NavLink> </NavItem> <UncontrolledDropdown nav inNavbar> <DropdownToggle nav caret> Options </DropdownToggle> <DropdownMenu right> <DropdownItem> <NavLink href="/donations/new">Donate</NavLink> </DropdownItem> <DropdownItem> Option 2 </DropdownItem> <DropdownItem divider /> <DropdownItem> Reset </DropdownItem> </DropdownMenu> </UncontrolledDropdown> </Nav> </Collapse> </Navbar> </React.Fragment> ); } } export default SampleNavBar
-
Add the SampleNavBar to the views/layout/application.html.erb
<body> <%= react_component("SampleNavBar") %> <div class='container-fluid'> <%= yield %> </div> </body>
-
Add bootstrap CSS to app/javascript/packs/application.js this will cause webpacker to compile and include the CSS into a webpack.
// Pull in the bootstrap css styles import 'bootstrap/dist/css/bootstrap.min.css'
-
Now let's create a small component for collecting the amout of the contribution. Create a file app/javascript/components/SimpleAmountType.jsx with the contents:
import React from 'react' class SimpleAmountType extends React.Component { render() { return( <div className="field"> <label>Amount: </label> <input type="text" name="donation[amount]"></input> </div> ); } } export default SimpleAmountType
-
We will next create a component that does not any payment information. This component should go in app/javascript/components/NoPayType.jsx with the following components:
import React from 'react' class NoPayType extends React.Component { render() { return(<div></div>); } } export default NoPayType
-
We create
CharitySelector.jsx
to use the other components that we created previously. The comments in the code segment explain the actions that will be taken.import React from 'react' import NoPayType from './NoPayType' import SimpleAmountType from './SimpleAmountType' class CharitySelector extends React.Component { // Add a contsturctor and bind constructor(props) { super(props); this.onCharitySelected = this.onCharitySelected.bind(this); // Initialize the state variable this.state = {selectedCharity: "none"}; } onCharitySelected(event) { // Event handler to change the state to the selected charity // see the select element in the render function below. this.setState({selectedCharity: event.target.value}); console.log(event.target.value); } render() { // Initialize the payment component to the NoPayType so no amount is field // is shown to the user. let PaymentAmountTypeComponent = NoPayType; if (this.state.selectedCharity != "none") { // If the state has changed to a type of payment, then we display // the SimpleAmountType component. PaymentAmountTypeComponent = SimpleAmountType; } return ( <div> <div className="field"> <label htmlFor="order_pay_type">Charity</label> <select id="charity" onChange={this.onCharitySelected} name="donation[charity]"> <option value="none">Select your favorite charity</option> <option value="habitat">Habitat for Humanity</option> <option value="unicef">Unicef</option> <option value="cancer_society">American Cancer Society</option> </select> </div> <PaymentAmountTypeComponent /> </div> ); } } export default CharitySelector
-
Update the app/views/donations/_form.html.erb by removing the charity field and adding the react component.
<%= react_component("CharitySelector") %>
-
Now let's do some cleanup. Since we are loading stylesheets with webpacker and not the asset pipeline, we can update the app/views/layout/application.html.erb file. to remove:
<%= stylesheet_link_tag 'application', media: 'all' data-turbolinks-track': 'reload' %> and add: <%= stylesheet_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
-
Adding Github Actions