Reusable Components

The React website says that, when designing your interface, you should "break down the common design elements into reusable components...That way, the next time you need to build some UI, you can write much less code." This idea works for elements like buttons and layout components, but falls down when it comes to navigational components like a data Sorter or Pager.

Navigational components contain Hyperlinks and Hyperlinks aren't reusable because their href attributes tie them down to a particular UI. The Navigation router's data-first approach sets Hyperlinks free so you can reuse them across UIs. I'll show how by building reusable Sorter and Pager components.

Making a Reusable Sorter Component

A reusable Sorter component consists of a Hyperlink that toggles the sort order. Remember that it's just the UI we're reusing and not the sorting logic itself. With the Navigation router we think in terms of typed data rather than URLs. We'll model the sort order as a boolean called 'up'. A value of true represents a sort direction of ascending. We can then use the Navigation router's RefreshLink component to build a Sorter component that toggles this 'up' value:

var Sorter = ({up, children}) => ( <RefreshLink navigationData={{up: !up}}> {children} </RefreshLink> );

This Sorter component isn't tied to a particular UI because there's no mention of any URL. It's reusable because we only operate on the 'up' data instead of hard-coding the Hyperlink's href. Where the component's used in the UI determines the value of the href. For example, on one page the 'up' data might appear in the query string and on another it could be a route parameter. You can see what I mean below where this Sorter component brings order to a list of fruit and vegetables:

Making a Reusable Pager Component

You've seen how the RefreshLink component lets us focus on the data instead of worrying about URLs. That means we can avoid hard-coding href attributes and create reusable navigational components. Let's use this technique to create a reusable Pager component. It will have 'First', 'Previous', 'Next' and 'Last' Hyperlinks that each change the current page number. For example, the 'First' Hyperlink sets the page number to 1 and the 'Previous' Hyperlink decrements the page number:

var Pager = ({page, size, total}) => { var pages = [ {text: 'First', page: 1}, {text: 'Previous', page: Math.max(page - 1, 1)}, {text: 'Next', page: Math.min(page + 1, Math.ceil(total / size))}, {text: 'Last', page: Math.ceil(total / size)} ]; return ( <div> {pages.map(pageInfo => <RefreshLink navigationData={{page: pageInfo.page}} disableActive={true} key={pageInfo.text}> {pageInfo.text} </RefreshLink> )} </div> ); };

Let's return to the fruit and veg example above and replace the Sorter with the Pager. Instead of using the 'up' data to sort the list, we'll use the 'page' data to split it across three pages:

Using the Sorter and Pager together

So far we've added our Sorter and Pager components separately to our fruit and veg example. If we tried to use them both together, so that the list can be sorted and paged, then we'd run into a problem. We'd find that when we toggle the sort order we lose the current page number. That's because the Sorter only manages the 'up' data and doesn't know anything about the 'page' data. But we can't pass the page number into the Sorter component because it won't be reusable anymore.

The Navigation router comes to our rescue. It keeps track of the current 'page' data so that the Sorter doesn't have to. To ensure that the sorting Hyperlink includes the current page number we set the RefreshLink's includeCurrentData prop to true.

var Sorter = ({up, children}) => ( <RefreshLink navigationData={{up: !up}} includeCurrentData={true}> {children} </RefreshLink> );