Let’s build a WordPress theme with React: Part 2 (Structure)

Now that we have our react app setup, let’s create our application structure. We’re going to need 3 main folders: 1) routes 2) partials and 3) context. These are all going to be inside react-src.

The routes folder is where we’re going to store the route files. These are the main files that are mapped in our url. So for example, if our url shows MYSITE/post/ – that means that’s rendering a file called Post.js inside our routes folder.

The partials contain bits and pieces of reusable parts. These are files that make up the header, navigaition, pagination or the loop.

The context is where the data sits. We’re going to have two files in here – Context.js and WithConsumer.js. These files are what we need to store data, as well as pass data down. More on this later.

Routes

Let’s begin with routes. Make sure we’re in development mode by running

npm run wpstart

Let’s create a new component called “Page”. Inside it, let’s start simple and put in the code below:

import React from 'react';

function Page() {
    return (
        <div className="Page">
            this is the page component
        </div>
    )
}

export default Page;

Pretty basic right? Now let’s install react router by running the command below:

npm install react-router-dom

Open up index.js and include react router in it

import { BrowserRouter, Switch, Route } from 'react-router-dom';

Inside the render function, let’s modify it to look like this:

ReactDOM.render(
<BrowserRouter>
    <Switch>
     <Route path='/page' component={Page}/>     
    </Switch>
  </BrowserRouter>, document.getElementById('root'));

Now navigate to your site, but append the path “/page”. You should see your page component rendered.

Now let’s go back to index.js and modify the Route to look like below:

<Route path="/page/:slug" component={Page} />    

Notice we’re adding a :slug to the path. We’re going to be updating our component with this parameter. The slug will be the actual “slug” of the WordPress post. If you modify our page component with:

<h1>this is the slug: {this.props.match.params.slug}</h1>

You should see something like below:

So by now, you should see how we’re going to continue with our theme. We’re going to be adding the following routes:

  • Home
  • Page + slug
  • Post + slug
  • Search + term
  • 404

Go ahead and copy the page component to match the routes above. I am keeping them in a folder called “routes” just to keep it organized. But you are free to structure your application any way you want:

Our index.js now looks like below:

import React from 'react';
import ReactDOM from 'react-dom';
import { Route, BrowserRouter as Router, Switch } from 'react-router-dom'
import './index.css';
import Home from './routes/Home';
import Page from './routes/Page';
import Post from './routes/Post';
import Search from './routes/Search';
import Notfound from './routes/Notfound';
import * as serviceWorker from './serviceWorker';

const routes = (
    <Router>
      <Switch>
          <Route exact path="/" component={Home} />          
          <Route path="/page/:slug" component={Page} />    
          <Route path="/post/:slug" component={Post} /> 
          <Route path="/search/:term" component={Search} />
          <Route component={Notfound} />       
      </Switch>
    </Router>
  )

ReactDOM.render(routes, document.getElementById('root'));

To browse the entire code up to this point in the tutorial, view this commit.

Partials

Now that we have our routes and components, it’s a perfect time to think about our partials. Partials are components that we will be reusing in each route component. Things that come to mind are header, footer, sidebar – and whichever reusable piece of code we will come across along the way.

Let’s store these files in its own folder called “partials“. Also, each route component will be using its own state. While partial components will rely on props. If you don’t know what the difference between state and props are, here is a good article about that.

Let’s go ahead and create 2 partials. Head.js and Foot.js. These will be the header and footer of our pages. The code will be a simple functional component like below:

/******Head.js*************/

import React from 'react'
function Head(){
    return (<div className="header">header</div>)
}
export default Head;

/******Foot.js*************/

import React from 'react'
function Foot(){
    return (<div className="footer">footer</div>)
}
export default Foot;

Now in our route components, simply import, and add the components in. Here is the how our Home.js will look like with the partials. Note – make sure you add them inside the main div:

import React from 'react'
import Head from '../partials/Head';
import Foot from '../partials/Foot';

class Home extends React.Component {
  render() {
    return (
      <div className="home">
          <Head></Head>
          <h1>Home</h1>
          <Foot></Foot>
      </div>
    )
  }
}
export default Home

In the browser, you will see our partials and home page starting to take shape. Inspect the code and it should look like below:

Do the same thing to the rest of our main components so they all share the same header and footer. You can see the code up to this point in this commit.

Context

As mentioned above, this is where our data will sit. React has Context API which allows you to have a centralized data store – so that you don’t have to pass data through each child component. This article is probably the best explanation on Context API. As a matter of fact, the two files that we’re going to build, is based on this article.

Let’s start with Context.js. This file is considered to be the “Provider”. Add the code below:

import React from "react";

const storeContext = React.createContext();
export const Consumer = storeContext.Consumer;

export class Provider extends React.Component {
  constructor(props) {
    super(props); 
     this.state = {
      posts : []      
    };
  }

 componentDidMount(){     
    this.setState({
       posts : ['title','title2']
    })
 }
 render() {
    return (
      <storeContext.Provider value={this.state}>
        {this.props.children}
      </storeContext.Provider>
    );
  }
} 

Don’t worry so much about the test posts data. We’re just using that for – well, testing. Then let’s create WithConsumer.js

import React from "react";
import { Consumer } from "./Context";

function WithConsumer(WrappedComponent) {
  return function(props) {
    return (
      <Consumer>
        {ctx => <WrappedComponent {...props} context={ctx} />}
      </Consumer>
    );
  };
}

export default WithConsumer;

Now this one is pretty confusing. It’s basically a wrapper – for our components that require the “Consumer” component. Basically, all our components that want to “consume” data from our “Provider” will be using this file.

Now let’s create a partial called “TheLoop“. Add the code below:

import React from 'react'; 
import WithConsumer from '../context/WithConsumer';

const TheLoop = ({ context }) => {

    const posts = () => context.posts;
    const pos = posts();
  
    return (
       pos.map(function(item,i){
         return <div key={i}>{item}</div>
       })
    ); 
};
export default WithConsumer(TheLoop);

Notice that in our export – we’re passing our component TheLoop as a parameter to WithConsumer.

Let’s open Post.js from our routes folder and modify it to look like this:

import React from 'react';
import Head from '../partials/Head';
import TheLoop from '../partials/TheLoop';
import Foot from '../partials/Foot';
import {Provider} from '../context/Context'
function Post (props) { 
  
  return (
    <Provider>
    <div className="Post">
      <Head></Head>
      <TheLoop></TheLoop>
      <Foot></Foot>
    </div>
    </Provider>
  )    
 
}
export default Post

You see how we’re wrapping everything in “<Provider>“, this is so we have access to our Context.js data. Also, we’ve included “TheLoop” as a component in between Head and Foot.

You should see “title” and “title2” in the browser by now.

Lastly, we need a way to get our “slug” parameter into our context. We can use props for that. Still in Post.js, pass the slug via props like so:

<Provider slug={props.match.params.slug}>

In Context.js, we should have access to the slug – through “props.slug“.

Let’s stop here for now. If you want to view the code at this point, see this commit.

We’ll pick it up from here next time.

Leave a Comment.