Daryll Doyle WordPress Development and Consultancy in Cornwall

Setting up and using Repress

S

In my last post, I discussed the general tech stack that I’ve been using lately for Headless WordPress builds. One of the main components of this stack is Repress.

Repress is a small library written by Ryan McCue from Human Made that works with Redux and allows you to access your WordPress REST API data easily and with minimum fuss. It contains a number of higher-order components (HOC) that reduce boilerplate code as well as enabling you to use the library manually for more complex tasks when needed.

Installing Repress

Installing Repress is pretty simple, it’s an NPM package that you can install using npm or yarn.

# For npm run
npm install --save @humanmade/repress

# For Yarn run
yarn add @humanmade/repress

Once you’ve installed the package, you’ll need to get it set up your handlers, this sets up the reducers and default actions for a post type (If I’ve not mentioned it before, Repress works via post type).

The official documentation for this, says to do it in a single file called types.js. For the first project I used Repress on, I followed this advice. Unfortunately, I found that the file became pretty large and confusing and as such, I now split out each post type’s handler into a separate file, E.G. posts.js, pages.js, users.js etc.

Setting handlers up is pretty simple, you import handler from the @humanmade/repress package and then use it as follows:

// posts.js
import { handler } from '@humanmade/repress';

export const posts = new handler( {
    // `type` (required): used to derive the action names, and typically should
    // match the object type (post type, taxonomy, etc).
    type: 'posts',

    // `url` (required): base URL for the type.
    // This will point to the WP API endpoint for this post type.
    url:   `${process.env.REACT_APP_WP_API_BASE}/wp/v2/posts`,
} );

You may have noticed a strange piece of code in there with the url. This piece of code ${process.env.REACT_APP_WP_API_BASE} is actually pretty cool as it allows you to set an environment variable in your .env file called WP_API_BASE and it’ll pull the value from there. This way, you don’t have to change the URL for each environment and can simply have a different .env file.

The URL, however you use it, will need to point to the WordPress REST API endpoint for the post type you’re setting up. In this case, its’ posts, so the URL will point to something like https://example.com/wp-json/wp/v2/posts. For pages, it would be https://example.com/wp-json/wp/v2/pages, you get the picture.

Anyway, once you’ve set up your handler, you’ll need to import it as a reducer into Redux. This section is very open and it’ll completely depend on your project setup, but somewhere you should have a combineReducers() call. At this point, you’ll want to import your handler from its file and add it to the reducer. Something like this:

// reducer.js
import { combineReducers } from 'redux';

// Importing from posts.js
import { posts } from './posts';

export default combineReducers( {
    // Any regular reducers you have go in here just like normal.

    // Then, create a substate for your handlers.
    posts: posts.reducer,
} );

At this point, your handler is now all connected up and you can repeat these steps for any other post types you may want to work with.

Retrieving single posts

To retrieve a single post using our new handler, we can use one of the HOCs that comes with Repress. This one is aptly named withSingle(). We’re going to continue on the same track and use posts as our example. I know React has gone over to functional components and they’re all the rage now, but I like ES6 classes and as such these examples will use them. Feel free to convert them over if you like though. /moan.

This is how I like to use Repress with a React component.

// SinglePost.js
import { withSingle } from '@humanmade/repress';
import React from 'react';
import { posts } from './posts';

class SinglePost extends React.Component {
    render() {
        // Get the post and loading props out of this.props.
        const { post, loading } = this.props;

        // If the post is loading, show a loading indicator.
        if(loading) {
            return 'Loading...'
        }

        // If the post isn't loading, display the post.
        return (
            <div>
                <h1>{post.title.rendered}</h1>
            </div>
        )
    }
}

// Export the component using the withSingle HOC.
export default withSingle(
    // Handler object:
    posts,

    // getSubstate() - returns the substate
    state => state.posts,

    // mapPropsToId - resolve the props to the post ID
    props => props.id
)(SinglePost);

It might look like there’s a lot going on here, but there really isn’t, so let’s walk through it. Firstly we’re importing the packages we’re going to use. React for our component class, withSingle is our HOC that connects this component to our handler and posts from our posts.js file we created earlier, as we’re going to use this in the HOC.

We then define our React component as usual, the only difference is that the HOC supplies some props of its own which we’re pulling out with the line: const { post, loading } = this.props;. These props are pretty self-explanatory, loading is a boolean value and lets us know whether Repress is currently fetching the post we’re trying to display. Whilst post is the result from the API as a JavaScript object. How easy is that?

The bit you may find slightly confusing is the HOC export, but it’s really not that hard. Lets walk through it: export default withSingle()(SinglePost) this is what actually links the HOC to our component. The withSingle() HOC then expects three arguments.

  1. The handler that should be used to fetch the data from the API. In this case, it’s posts from posts.js. That’s why we needed to import it at the start.
  2. The second is an odd getSubstate() function and this is what took me the longest to get my head around with Repress. In this example, it’s written as state => state.posts but it could quite easily be written like function( state ){ return state.posts }. It’s basically a function that receives the current Redux state and needs to return the location of the substate for the resource we want. In our example, this is state.posts but it’s generally the object key that the handler was registered under when you called combineReducers() earlier. E.G. for pages, it may look like state => state.pages.
  3. This is where you’re getting the ID for the post from and is another shorthand function like the last parameter. This isn’t so confusing though and I find it’s easiest to stick with this as the default. This default just says, look at the props.id prop to get the post ID.

If you use the default for #3 above, you can then call your component like this <SinglePost id="3" /> and this will then fetch and render the post with the ID of 3. Can this get any easier?

Post type archives

Funnily enough, archives are also pretty simple in Repress and the work we did for the single posts, has set up most of what we need for the archives. The only difference is that we need to register our archives separately to the handler, but this isn’t a hard thing to do. Let’s head back over to our posts.js file and see what we need to do.

As you recall, our posts.js file looks something like the below:

// posts.js
import { handler } from '@humanmade/repress';

export const posts = new handler( {
    // `type` (required): used to derive the action names, and typically should
    // match the object type (post type, taxonomy, etc).
    type: 'posts',

    // `url` (required): base URL for the type.
    // This will point to the WP API endpoint for this post type.
    url:   `${process.env.REACT_APP_WP_API_BASE}/wp/v2/posts`,
} );

This is all good for the single posts, but let’s look at what we need to do for archives.

Firstly, Repress’s handler class, ships with a very useful method, called registerArchive(). The registerArchive() method accepts two arguments, the first is the name of the archive as you want to save it (any scalar value will work here) and the second is the query that you’d like to register to the archive. For example, if we’re registering an archive to pull all sticky posts out, you may use something like this:

posts.registerArchive( 'stickied', { 
    per_page: 3,
    sticky: '1',
} );

In this example, we’re registering an archive called stickied and passing through a query of sticky=1and per_page=3. The sticky=1 parameter, tells the API to only supply posts that are marked as sticky. Whilst the per_page=3 parameter, as you may have guessed, tells the API to give us three posts per page. This is very useful for pagination, which I’ll cover in a bit.

The query department is fairly straightforward and you can use a lot of query parameters that you might need to use. For a full list of parameters that you can use, see the official docs. You may, as I did, run into the need to use some parameters that aren’t available by default, E.G. if you want to query meta data or something more complex. In this case, you can install and activate the WP REST Filter plugin, which enables you to use all the public query vars of WP_Query to filter your API request with. For example, we can now do something like this:

posts.registerArchive( 'key_resources', { 
    per_page: 6,
    filter: {
        meta_key: 'key_resource',
        meta_value: 1
    },
} );

In this example, we’ve asked for 6 items per page (the per_page parameter). We’ve then got the new filter parameter, that the WP REST Filter plugin enabled us to use. Within this filter param, we’re passing through a meta query, in this case, we’re asking for any posts with the meta key of key_resource and the meta value of 1. This is an actual example of one I’ve used in a project and the meta_key actually relates to an Advanced Custom Fields field (if that’s of any help to you).

Now we’ve covered registering archives, lets look at how we consume them, once again using the HOC. This time, we’ll be using the withArchive() HOC. A lot like withSingle(), withArchive() takes three arguments:

  1. The handler object, as per withSingle(), we’ll be using posts from posts.js.
  2. Again, the second argument is the same as the second argument in withSingle(). It’s a function that returns the specific sub-state for the post type we’re calling. In this case, it will look like this: state => state.posts.
  3. This is where withArchive() differs slightly, instead of passing through a post ID, we’ll pass a string containing the archive ID that we gave it when we registered it. For example, we registered stickied and key_resources, in our example, we’ll use the key_resources archive.

Using the withArchive() HOC is pretty much the same as using the withSingle() version, it’s just the render function and props will look slightly different.

// PostArchive.js
import { withArchive } from '@humanmade/repress';
import React from 'react';
import { posts } from './posts';

class PostArchive extends React.Component {
    render() {
        // Get the posts and loading props out of this.props.
        const { posts, loading } = this.props;

        // If the archive is loading, show a loading indicator.
        if(loading) {
            return 'Loading...'
        }

        // If the archive isn't loading, loop over the posts and display them.
        return (
            <ul>
                { props.posts.map( post =>
                    <li key={ post.id }>
                        { post.title.rendered }
                    </li>
                ) }
            </ul>
        )
    }
}

// Export the component using the withSingle HOC.
export default withArchive(
    // Handler object:
    posts,

    // getSubstate() - returns the substate
    state => state.posts,

    // Archive ID
    'key_resources'
)(PostArchive);

Let’s walk through this code then:

At the top, we have our imports, we’re importing the withArchive HOC from Repress, React for our class and posts so that we can use it later in our HOC.

We then define our ES6 class and start our render() function. Inside this, we’ll destructure the properties that we need from this.props. In this case, it’s posts that will contain an array of posts from the archive and loading which once again will give us a boolean value to tell us whether the archive is loading or not.

Archive pagination

So far, we’ve set up a single post and a basic post archive, but what happens if we want to offer pagination or a “Load more” option to the user? Luckily for us, Repress also makes that easy.

We can build upon our current archive page to do this and it really only requires destructuring a few more properties, ready for it?

// PostArchive.js
import { withArchive } from '@humanmade/repress';
import React from 'react';
import { posts } from './posts';

class PostArchive extends React.Component {
    render() {
        // Get the posts and loading props out of this.props.
        // Also now getting hasMore and loadingMore
        const { posts, loading, hasMore, loadingMore } = this.props;

        // If the archive is loading, show a loading indicator.
        if(loading) {
            return 'Loading...'
        }

        // If the archive isn't loading, loop over the posts and display them.
        return (
            <div>
                <ul>
                    { props.posts.map( post =>
                        <li key={ post.id }>
                            { post.title.rendered }
                        </li>
                    ) }
                </ul>

                { loadingMore ?
                    <p>Loading more...</p>
                : hasMore ?
                    <button
                        type="button"
                        onClick={ () => this.props.onLoadMore() }
                    >Load More</button>
                : null }
            </div>
        )
    }
}

// Export the component using the withSingle HOC.
export default withArchive(
    // Handler object:
    posts,

    // getSubstate() - returns the substate
    state => state.posts,

    // Archive ID
    'key_resources'
)(PostArchive);

Let’s go through those new bits:

We’re now destructuring two more properties from this.props. We’re getting the hasMore property, which is a boolean value letting us know if there are more posts to be retrieved in this archive. We’re also getting the loadingMore property, again this is a boolean value, but this time it tells us whether we’re in the process of fetching more posts from the archive.

The only other new bit of the class is within the render function and looks like this:

{ loadingMore ?
    <p>Loading more...</p>
: hasMore ?
    <button
        type="button"
        onClick={ () => this.props.onLoadMore() }
    >Load More</button>
: null }

This is actually just a few shorthand if statements put together. We first check to see if we’re loading more posts using the loadingMore variable, if we are, we show Loading more… If we’re not loading more, we check to see if we have more posts, using the hasMore variable. If there are no more posts, we return null which in React will render nothing. If we do have more posts, we show this button:

<button
    type="button"
    onClick={ () => this.props.onLoadMore() }
>
    Load More
</button>

Hopefully, you can now see what this is doing. We’re outputting a button with the text Load More. This button has an onClick handler that calls an arrow function, which in turn calls a Repress method from the withArchive() HOC, this method is onLoadMore(). We’re accessing it via this.props.onLoadMore() but we could have also destructured it and called it as a variable.

The onLoadMore() method, handles a lot in the background for you.

  • Making the API call for more posts from this archive
  • Updating the Redux store with the new posts
  • Updates the internal Repress store to keep track of what page you’re on

There you have it, simple Load more style pagination. If you think you can do this in less code, then please show me how!

As a note, you can also do manual pagination pretty easily in Repress, but I’ll refer you to the official documentation for this as it’s not too much different than using the load more style.

What’s next?

I have quite a lot to say about Repress, I’ve used it a fair amount recently in both big and small projects. In my next post, I’m hoping to cover a little more about dynamic archives in Repress and how I’m using them.

If there’s anything else people would like to know, feel free to reach out to me on Twitter at @enshrined!

About the author

Daryll

Daryll is a WordPress developer, currently building themes and plugins for Built by Cactus.

In his spare time, he works on his SVG sanitisation plugin Safe SVG, that currently has over 100,000 installs from the WordPress.org plugin directory.

He has also spoken publicly about SVGs and WordPress, including a talk at WordCamp London 2018.

Add comment

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Daryll Doyle WordPress Development and Consultancy in Cornwall