« Previous Tutorial Next Tutorial »

Welcome back to another fabulous episode of Five Minute React. In our last tutorial, we installed Redux and talked about how it works. Now we’re going to get started wiring it into our React app. To start, we’re going to set up our Store. This doesn’t mean we’re going to be leasing a space and filling shelves with consumer goods, but rather creating the files we need to manage our application’s state.

As previously mentioned, Redux calls your application’s state the Store, because it’s where your application’s active data is stored. Flux, the general paradigm Facebook established for working with React using a one-way data flow, was originally designed to use multiple stores (one for each group of data you want to work with). That’s fine, but Redux simplifies things a bit. Your Store is one massive JavaScript object, and you use individual files called “reducers” to adjust it. These files take incoming actions—remember, those are just JavaScript objects with a description and any relevant data—and “reduce” them down into data for the Store.

Seem confusing? Fear not! We’ll work our way through it. To build out our Store, let’s first think about what we want our app state to look like. Right now the only thing we need to track (and, for that matter, have the API endpoints necessary to track) is a user’s logged-in state and some basic info about them. Well, that’s not quite true; we also want to track whether an action is in progress, which doesn’t require an API endpoint.

But that’s too much to start with. Trust me. So here’s what our Store’s going to look like at first:

{
  progress: 0,
}

Now that’s straightforward! Forget users entirely for a few tutorials, forget logging in … first thing we’re going to do is just get our application talking to our Store with the simplest amount of state data possible. We can do this. It involves touching a total of five files and building some new stuff, but we’ll break each piece down and explain as we go along.

First, create a file under /src/reducers called progress.js. We’re not using .jsx here because reducers are pure JavaScript. They never contain jsx code or have anything to do with the display layer of our application. They’re pure data handlers.

Here’s what progress.js should look like:

const initialState = 0;

export default function reducer(state = initialState, action) {
  switch (action.type) {
    default: {
      return state;
    }
  }
}

Not much going on there right now, which is nice. Keeps things simple. We set an “initialState” variable to zero (I’ll explain why we’re using an integer here when we get to building actions), then ingest that variable into our reducer function using a nifty ES6 feature where a function argument can take either a value or, if that value is null, assign a default. We’re also bringing in an action argument, which right now is always going to be undefined, but that’s fine, since the only thing our switch statement is doing is going “no matter what, just return the existing state, which is zero, because that’s the default when we don’t pass anything to the state argument.”

So progress is always going to be zero, for right now. That’s what we want.

Save that file and then create a folder under /src called “store” … this folder will contain one file. I realize that’s kind of stupid, but it helps keep your brain wrapped around what’s coming from where. The file you want to put in /src/store is simply called index.js. This will simplify imports later on.

OK, in this file, you want the following code:

import { combineReducers, createStore } from 'redux';

import ProgressReducer from '../reducers/progress';

const combinedReducers = combineReducers({
  progress: ProgressReducer,
});

const Store = createStore(combinedReducers);

export default Store;

Line by line, here’s what’s going on. First we’re importing two methods from Redux. The combineReducers function is used to mash all of our individual reducers into a single big object. The createStore function turns that object into our Store, which helps wire up a lot of the React automagic .

On line 3 we’re importing our progress reducer from the reducer we just built. As we know, that’s going to return zero every time, for now. On line 5 We’re then creating a combined reducer variable using Redux’s combineReducers function and setting our app state’s “progress” value to whatever our progress reducer gives it … which, again, is currently always zero.

On line 9, we’re creating our Store from the combined reducers we just created. Voila, we have app state, and on line 11 we export it for use in the rest of our app.

Save this file. Now create a file in /src/components called TemplateContainer.jsx (capital at the start, as always with components). Why are we creating a container for our template? Well, because we need to “connect” to part of our Store in order to pull data from it and then pass that data down as props. Whenever you want to connect a component, it’s best to create a container component. That way, all of your logic lives in the container, and the component itself remains a “dumb” component – all it does is display data passed down to it as props. It’s not strictly necessary, and I’ve worked on projects that don’t use containers, but I think they really help keep logic and display separate and clean.

So here’s the code for your Template Container:

import React from 'react';
import { connect } from 'react-redux';
import Template from './Template';

function TemplateContainer(props) {
  return (
    <Template progress={props.progress} />
  );
}

function mapStateToProps(state) {
  return {
    progress: state.progress,
  };
}

export default connect(mapStateToProps)(TemplateContainer);

All right, lots going on here. Let’s dig in. The top three lines import React, the connect functionality we’re going to need from Redux to connect to our Store, and our Template, which we’ll be calling like any ordinary React component. Then we have a pure function to render our TemplateContainer component, which takes a “props” argument. Then we pass the progress value derived from our props on to the template. Where’s that value coming from? Further down!

Our next function takes state data that exists in the store, and turns it into a JavaScript object. You can name this function whatever you like, but “mapStateToProps” is default Redux nomenclature, and I recommend using it, since that’s what you’ll see in just about any other Redux app you ever look at.

Finally, we’re exporting our component … except we’re running it through Redux’s “connect” functionality first. This is where the magic happens. It uses our MapStateToProps function to bind a piece of application state from the Store to our component’s props … without overwriting any other props we might’ve sent down from whatever higher-level component called this one! This is crazy useful not only because it lets us access app state easily, but because it will trigger a component re-render every time app state changes … this means if we increment or decrement our progress integer, the prop will change, the component will re-render, and we can do something with that new data.

And we’ll do that in the very next tutorial. For now, we’re going to wrap up the final bits we need here. Save this file, then open up /src/components/Template.jsx and make two small changes, one of them extremely temporary. First, on line9, add a props argument like this:

export default function Template(props) {

Then, just below the header on line 13, add this line:

        <p>{props.progress}</p>

This is going to display “0” on the page once we’re all set, proving that our app is now attached to our Store. Save this file, and open /src/index.jsx … it’s time to bring in our TemplateContainer component, instead of our Template component (which is now called by the container). Adjust the top five lines to look like this:

// Default export from a module
import React from 'react';
// Individual method exports from a module
import { AppContainer } from 'react-hot-loader';
import { Provider } from 'react-redux';
import { render } from 'react-dom';

// CSS from a module
import 'bootstrap/dist/css/bootstrap.css';
// CSS from a local file
import './css/musiclist.scss';

// Default export from a local file
import Store from './store';

That adds a “provider” component we need from Redux and also reorganizes things to be in a more logical order (I added a couple of comments to help explain the ordering). It also imports that Store we created earlier. Now change line 16 from

import Template from './components/Template';

to:

import TemplateContainer from './components/TemplateContainer';

Then wrap line 21 in Provider tags, and while we’re at it, remove our test props, like this:

      <Provider store={Store}&ht;
        <Component />
      </Provider>

Change line 29 from:

renderApp(Template)

to:

renderApp(TemplateContainer)

And then change lines 32-34 from:

  module.hot.accept('./components/Template', () => {
    renderApp(Template);
  });

to:

  module.hot.accept('./components/TemplateContainer', () => {
    renderApp(TemplateContainer);
  });

Your final file should look like this:

// Default export from a module
import React from 'react';
// Individual method exports from a module
import { AppContainer } from 'react-hot-loader';
import { Provider } from 'react-redux';
import { render } from 'react-dom';

// CSS from a module
import 'bootstrap/dist/css/bootstrap.css';
// CSS from a local file
import './css/musiclist.scss';

// Default export from a local file
import Store from './store';

import TemplateContainer from './components/TemplateContainer';

const renderApp = (Component) => {
  render(
    <AppContainer>
      <Provider store={Store}>
        <Component />
      </Provider>
    </AppContainer>,
    document.querySelector('#react-app'),
  );
};

renderApp(TemplateContainer);

if (module && module.hot) {
  module.hot.accept('./components/TemplateContainer', () => {
    renderApp(TemplateContainer);
  });
}

That’s it. Save the file, and you’re good to go. You will very definitely have to do a hard refresh here, even if you’re server’s running, since we’ve made changes to the core of how everything’s working. Once you do so, however, you should see your little zero pop up.

OK, so, it’s not the most amazing thing ever, but just wait till next tutorial, when we … make that zero change to a one and back

Isn’t coding exciting?

Catch you soon.

« Previous Tutorial Next Tutorial »