React WP theme: Smart vs Dumb components + React Router

In the third installment of our “How to make a React-powered WP theme” tutorial, we create the starting point of our React-powered WordPress theme. I’ll show you what all we need to import in order to have a working application. We’ll also talk about how to properly divide the React components into smart and dumb ones.

Tutorial navigation

  1. React single-page WordPress REST API theme tutorial
  2. React WP theme: structure, Node packages and Webpack
  3. React WP theme: Smart vs Dumb components + React Router (current)
  4. React WP theme: Creating dumb components
  5. .
    . to be written
    .

The ‘src’ folder

Create a new folder in the root directory of our theme and call it src. The complete application will live in it. Now, create these files and folders:

actions/
components/
containers/
reducers/
store/

index.js
index.template.html

I won’t go into detail about the actions, reducers and store folders as they relate to the Redux library, which we’ll talk about in further articles.

Now, let’s go through the two files and see what they are all about.

index.template.html

This file serves as a template for the HTML Webpack Plugin (read the previous post in the tutorial if you don’t know what that is). It’s just an HTML5 skeleton with one

 where our app will mount:


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="root"></div>
</body>
</html>

index.js

index.js is the main file of our application. In it, we load all the React components, configure the main Redux store and most importantly set up the app routes with React Router.

I’ll paste the entire file here and will go through it, line by line:


import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import createBrowserHistory from 'history/lib/createBrowserHistory';
import { Provider } from 'react-redux';
import { Router, Route, IndexRoute } from 'react-router';
import configureStore from './store/configureStore';
import LexiTheme from './containers/LexiTheme';
import ArticlePage from './containers/ArticlePage';
import ArticleListingPage from './containers/ArticleListingPage';
import AboutPageContainer from './containers/AboutPageContainer';
import '../sass/bootstrap.css';
import '../sass/bootstrap-blog.css';
const history = new createBrowserHistory();
const store = configureStore();
let rootElement = document.getElementById('root');
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={LexiTheme}>
<IndexRoute component={ArticleListingPage} />
<Route path="about" component={AboutPageContainer} />
<Route path=":year/:month/:name" component={ArticlePage} />
</Route>
</Router>
</Provider>,
rootElement
);

view raw

index.js

hosted with ❤ by GitHub

Importing the important stuff

Have you noticed the countless import statements at the beginning of the file? Yeah, if we wanted to be less verbose, we could group them by usage into separate files and import just these, but this way it’s easier to understand and explain.

Firstly, we import React and Component from the react package. We use them to create a React component in the ES6 class manner, like this:

class MyReactComponent extends Component {
}

Looking at the index file, actually, we don’t really need that. 😛

Next is ReactDOM. It bridges the DOM with the React so we can now render the components into the DOM. Look at line 18. There, we call the ReactDOM.render() method, which renders the nested component into the DOM. react-dom used to be one library with react but they’ve decided to separate the two as programmers use React for other things than just creating web pages (e.g. React Native for building mobile apps).

createBrowserHistory is a small lib for managing the history of navigation in our browser. It uses the HTML5 History API if possible and falls back to hash history if not. If I remember correctly, this used to be a part of the react-router library but it’s more modular when separated.

Lines 4 and 6 are not important at the moment (Redux-related). Will talk about that in the next article.

import { Router, Route, IndexRoute } from 'react-router imports all the necessary modules for working with React Router.

React Router

React Router is a brilliant though very simple idea which makes routing in our app a walk with a breeze. It is similar to how we specify routes in Ruby on Rails or Django. It works like this:

  • Everything is a standard React component.
  • “ is a wrapper around the routes. Its purpose is to initialize the routing system with a specific history API. That’s why we need to provide the `history={…}` attribute to it.
  • “ component points to a particular `path` (location in URL) and if that path is matched, it renders the component provided in the `component={…}` attribute.

And that’s basically it!

Let’s have a small example, shall we?

We have a small site (www.example.com) with three pages: home, about and contact. These pages are represented as three React components (i.e. Home, About and Contact). We add the following routing into the app:

    
    
    

Now, when we visit the http://www.example.com/home URL, React Router will look at the defined routes and match the first one (path="/home"). We assigned the Home component to the first route, so React Router will render it.

When we visit the http://www.example.com/about, it will behave exactly like before except rendering the About component instead of Home.

It’s very easy, isn’t it? But back to our code index.js.

Index.js continued

In the file index.js, we have a bit more complex React Router definition:

    
    
    

We can nest routes like other standard React components. It means that we don’t have to specify the whole path to the route — if the component is located “under” the path, we simply nest it.

In the code above, the path="/" is the index route. All the other application routes are located “under” it, that’s why the s representing them are nested inside it. Now, when we define the about page route, we don’t have to specify the initial slash character (/) because the route is nested under the root route with path="/".

“But”, you say, “what if we navigate to the /about URL? Which component will render? The AboutPageContainer or the LexiTheme one?”. Great question! As we nest the “about” route under the “root” route, React Router will render the LexiTheme component. However, it will pass AboutPageContainer as its children. If you know what React children components are, you surely know that in each component, we can access its children through the props like this: this.props.children. Then it’s just a matter of rendering the children components in the parent component, like this:

{this.props.children}
<Sidebar /> </div> </div> <Footer />

So the logic here is that we have a single parent component (like LexiTheme) which renders the main navigation, header, sidebar, footer, etc. and its children which will change according to which nested route was accessed.

Phew, I hope you could understand what I was trying to say. Anyways, React Router docs explain it better than I ever could.

Smart vs dumb React Components

Let’s get back to the import statements. Notice that we import some React components from the containers/ folder (e.g. LexiTheme, AboutPageContainer). But the folder components/ holds React components too (obviously, according to its name). So what’s the difference between these two folders?

Initially, I was pretty confused. Then I read Dan Abramov’s article about Smart vs Dumb components and it clicked. To sum it up, we should divide React components into two groups: the smart ones and the dumb ones.

The smart component contains the logic, gathers data, performs computations. The dumb component is rendered from the smart one, passed the final data and just renders the view (HTML). Smart components are specific to a framework and usually can’t be reused easily. On the other hand, dumb components just render some HTML so they can be reused easily.

When we make this distinction, our app and components become reusable and clear. Hopefully.

So, there is a convention that the smart components reside in the containers/ folder while the dumb ones in components/ one. And that’s it.

Wrapping it up

Finally, we import the Bootstrap’s CSS files. The nice thing about Webpack is that it will simply import CSS files, compile them and put them in a separate .css file. We discussed this in the previous article in this series.

After importing all the required objects and modules (a lot of them), we instantiate the createBrowserHistory object, configure Redux stores (will talk about this in a further article) and find the root <div> (the one <div> we placed in the HTML template at the beginning of this post).

The last step is to render React components with ReactDOM.render() method. It takes two arguments: the component to render and the HTML element where to render it.

Join the Conversation

5 Comments

  1. Great post! I’ve been using Sage as a theme and as I started to construct my own React WP Theme I was trying to figure out a way to create a component wrapper similar to their template wrapper. The container/component thinking makes a lot of sense and, it worked! Keep the articles coming!

    Like

    1. Thanks!

      Cool, I’ve played with Sage in past too. Their template wrapper is really nice.

      Yeah. Initially, I was a bit confused about the difference between containers and components, that’s why I’m trying to emphasize the distinction between them all the time.

      As for the articles, I’m a little bit off schedule but I’ll finish them. In the meantime, you could study the source code of my theme https://github.com/lamosty/lexi/tree/master/src. It might help you with your project. Also, when I started doing this, I went through all the examples in Redux as well as some React Router ones. It helped a lot!

      Like

      1. yeah, it’s a huge learning curve but it’s going to be awesome to see JS really thrive in the WP ecosystem moving forward.

        Like

  2. Hello, awesome tutorial! Thank you for this. It looks like some of this page got messed up somehow. I think some of the code on the second half of the page got erased somehow.

    Like

  3. Awesome article! As a wordpress developer and oldschool php guy, I always wanted to gain all benefits from modern js frameworks. Unfortunately some of examples in

    
    

    tags are empty or not full.

    Liked by 2 people

Leave a comment