Using Redux with react_on_rails

✨ 1 person thanked Hrishi Mittal for this tutorial.

👍 Send Thanks
 
The React on Rails gem comes bundled with support for Redux.

In this lesson, we’ll look at the example HelloWorld app that comes with the react_on_rails gem and see how Redux can be used in a simple React app.

We'll create a brand Rails app for this.

Let's say rails new and let's call it rails-redux

rails new rails-redux

After installing the react on rails gem,

Add the gem to your Gemfile:

gem 'react_on_rails'

Let’s run the gem’s generator with the —-redux option

rails generate react_on_rails:install --redux

This automatically sets up our app with redux.

Then, let's commit the code to git and run bundle && yarn

bundle && yarn

And then start the app with foreman

foreman start -f Procfile.dev

Now, if we open the app in the browser and go to /hello_world

localhost:5000/hello_world

You can see this react component and its working with redux.

So, now let’s look at the code and see how this works.

Let’s start by looking at the index erb template.

Here we are rendering this HelloWorldApp component using the react_component helper method.

<%= react_component("HelloWorldApp", props: @hello_world_props, prerender: false) %>
And, we’re passing these hello_world_props to it.

We can see what the props are in the controller.

class HelloWorldController < ApplicationController 
    def index 
        @hello_world_props = {name: "Stranger"} 
    end 
end

And, it’s just one prop called name with a string value Stranger.

Now, let’s look at the HelloWorldApp component.

We can see that it’s being registered in the startup/registration file

import HelloWorldApp from './HelloWorldApp';

And, its location is in the same directory.

So, let’s open that.

So, this is where you get the props from the Rails view and you connect it to the Redux store.

const HelloWorldApp (props, _railsContext) => (
    <Provider store={configureStore(props)}>
	    <HelloWorldContainer />
	</Provider>
);

This is a functional component, which wraps the Provider component from react-redux around this HelloWorldContainer component, which contains the actual UI for our app.

Here the store is specified as a prop of Provider and its value is set based on the props passed to HelloWorldApp, in this case the name prop.

So now, let’s look at each of these parts one by one to understand the data flow and see how it’s all working.

Up here, the Provider component is imported from react-redux.

import { Provider } from 'react-redux';

And then configureStore is imported from HelloWorldStore. As the name suggests, this sets up the Redux store.

import configureStore from '../store/helloWorldStore';

And then we have a HelloWorldContainer, which is a smart component in this example.

import HelloWorldContainer from '../containers/HelloWorldContainer';

Now let’s look at configureStore in HelloWorldStore.

Here we first import a function called createStore from redux.

import { createStore } from 'redux';

Which is then used in this method configureStore to create a new store.

const configureStore = (railsProps) => (
    createStore(helloWorldReducer, railsProps)
);
createStore takes two arguments - helloWorldReducer and railsprops.

helloWorldReducer as the name suggests is a reducer that is a pure function

Which, takes some state data and an action.

Applies the action to the data and returns some new data.

Let’s have a look at the helloWorldReducer file.

In this example, there’s only one reducer function.

You can have multiple reducers for different interactions in your app.

And then, you use this combineReducers function from redux to combine them all into one reducer.

const helloWorldReducer = combineReducers({name});

Here we just have one reducer called name, which is passed to combineReducers.

const name = (state = '', action) => {
    switch (action.type){
        case HELLO_WORLD_NAME_UPDATE:
            return action.text;
        default:
            return state;
    }
}

Name is a very simple function.

It checks for the type of action passed to it.

If it’s of type HELLO_WORLD_NAME_UPDATE, then it simply returns the value of the text property of the action.

If you remember, the action in this case is just someone typing into this input field.

Here HELLO_WORLD_NAME_UPDATE is a constant which is coming from here.

Its value is just a string.

This is a common pattern used in Redux to define action types.

Okay, now let’s look at HelloWorldContainer.

This is where we’ll see where the action is coming from.

Here we define a function called mapStateToProps, which as the name suggests, defines which part of the global state this component is going to receive as props.

const mapStateToProps = (state) => ({name: state.name});

Remember, we talked about views subscribing to change events from the store. 

This is what’s happening here with this connect function call.

export default connect(mapStateToProps, actions)(HelloWorld);

It takes 2 arguments - mapStateToProps and actions.

The return value of connect is another function, also known as a Higher-order React component, which takes a component and returns a new component.

So, that’s why we have two sets of parentheses here.

The first one returns another function, which we then call with the HelloWorld component as its argument, which then returns a new component.

So, now let’s look at the HelloWorld component.

This is a presentational component, which accepts two props, name and updateName, which are coming from here, name and actions.

Actions is coming from this file called helloWorldActionCreators

So, let’s have a look at that now.

Again, this is just a pure function called updatename which takes some text and returns an object with two keys type with the value HELLO_WORLD_NAME_UPDATE and text.

export const updateName = (text) => ({
    type: HELLO_WORLD_NAME_UPDATE,
    text,
});

By the way, this is a shorthand for writing text: text

So, we’re passing name and updatename as props to the HelloWorld component.

which then uses them in this JSX code.

const HelloWorld = ({name, updateName}) =>(
<div>
    <h3>
        Hello, {name}!
    </h3>
    <hr />
    <form>
        <label htmlFor="name">
            Say hello to:
        </label>
        <input
            id="name"
            type="text"
            value={name}
            onChange={(e) => updateName(e.target.value)}
        />
    </form>
<div>
);

There’s an H3 tag here, inside which name is displayed.

And, then there’s a form with an input field.

Which, has value name and an onChange function.

Which, calls updatename with the value of the field

So, when we type in the field, the action updatename is dispatched.

The store uses the data and action type to update the state.

And, emits a change event.

Which, this component is subscribed to. 

And, the name gets updated in the H3 tag.

import * as actions from '../actions/helloWorldActionCreators';

Alright, so that’s a very simple example of using Redux with React.

This is obviously a toy example.

You wouldn’t really use Redux in such a simple app.

There’s no need for it.

But, I wanted to use it to show you how it works.

Typically, if your app becomes complex enough that you want to use Redux.

You’re probably better off using Redux in a separate React app, not inside Rails.

Liked this tutorial? Get more like this in your inbox