Share Code between React and React Native Apps
Developers are adopting Higher Order Components (HOC) Stateless Functional Components, and for good reason: they make it easier to achieve code reuse, a coveted aspiration of developers.
There are many articles on HOC and Functional Stateless Components. Some are introductions and others describe deep technical aspects; I’ll explore refactoring existing components to create reusable elements.
You might think that code reuse is overrated. Or it’s too hard, especially when looking to share code between the web and mobile. But here are a few benefits to consider:
- UX consistency, both within an application and across devices
- Make cross-cutting upgrades: improve a component and update all its uses easily
- reuse routing and authorization rules
- Switch libraries (for example, the apps below uses MobX for state management, but Redux could be swapped in)
I’ll focus on using HOC and Functional Stateless Components to achieve reuse. You should already be familiar with the basics of React and React Native. Alexis Mangin has a good post explaining their differences as well.
There is a lot of detail in the post; I explain the incremental process for refactoring the components. But if you are already familiar with these ideas (such as HOC), short on time, or just impatient, you can jump ahead to The Payoff: Reusing the Components. (Final GitHub repo) You can see the result and how easy it is to create additional applications with the reused components.
What are Higher Order Components and Stateless Functional Components?
React 0.14 introduced Stateless Functional Components. They are functions that render components. The syntax is simpler; there are no class definitions nor constructors. And as the name implies, there is no state management (no uses of setState
). I’ll comment more on this later and defer examples till later in this tutorial.
Cory House has a good introduction here.
Higher Order Components (HOC) are functions that create a new component. They wrap another component (or components), encapsulating the wrapped components. For example, let’s imagine you have a simple text box. You’d like to add autocomplete functionality. You could create a HOC and use the result as a new component.
const AutocompleteTextBox = makeAutocomplete(TextBox);
export AutocompleteTextBox;//…laterimport {AutoCompleteTextBox} from ‘./somefile’;
The Facebook documentation is here. franleplant has a detailed post as well.
We’ll use both HOC and Stateless Functional Components in a few moments.
Sample Application
We’ll start with a very simple application: a simple search box. Enter a query and get a list of results. In this case, we’ll search for colors, by name.
It will be a one screen application. It will not use routes nor multiple scenes as the focus is on component reuse.
We will add a second pair of applications (React and React Native), which will reuse the components we extract.
This GitHub repo branch has the baseline applications (The final result is here.). I include the full details on building the React (web) and React Native (mobile) apps in the GitHub README, but here is an outline:
- create-react-app bootstraps the React application
- I use Material UI for the UI elements in the React/web app
react-native init
bootstraps the React Native application- I use MobX for state management. (Michel Weststrate, creator of Mobx, has good tutorials here and here.)
https://colors-search-box.firebaseapp.com/ is a running demo of the web version. Screenshots of both are below (web, then mobile):
Refactoring to Reuse
Achieving Code Reuse is About Perspective
The basics of code reuse are simple. You extract methods (or classes or components) from one code base, replacing enclosed values with parameters. You then use the result in another code base. But the mileage of the reused element is often low and maintaining the shared code can become costly.
I’ve achieved the sustained reuse by applying a few guidelines: Separation of Concerns, Single Responsibility Principle, and the removal of duplication.
Separation of Concerns (SoC) and Single Responsibility Principle (SRP) are two sides of the same coin; the main idea is that a given code element should have one primary purpose. If there is one purpose, separation of concerns is a natural by-product; an element with one purpose probably won’t mix two areas of responsibility.
Many IDE’s and developer tools can automate the consolidation of duplicate code. But removing duplication amongst similar designs is more difficult. You have to “see” the duplication, which might require rearranging code blocks.
Applying these ideas is like moving puzzle pieces around, to find where they meet and what patterns they reveal.
Let’s start by looking for duplication.
Seeing Duplication
The web and mobile applications have two main components.
In the web application, App.js
In the mobile application, SearchView.js
The following outlines their structure.
The two components have similar structures. Ideally they would share components and look something like this:
And in pseudo-code,
Unfortunately though, in the two applications, there is very little actual code in common. The components used in React (Material UI in this case) are different from those in React Native. But we can remove the conceptual duplication by first separating concerns and then refactoring the components to each have a single responsibility.
Separation of Concerns and Single Responsibility
Both App.js
and SearchView.js
mix domain logic (our app logic) with the platform implementation and library integrations. We can improve the design if we isolate
- UI implementation: e.g. separate
ListItem
andListView
from the concept of search results - UX from state changes: e.g. separate submitting a search from updating and showing the results
- components: search input, search results (list) and an individual search result (list item) should each be a separate component
Finally, refactoring should be done with automated tests, to ensure nothing breaks as you make changes. I’ll add some simple “smoke” tests, which can be found in this GitHub repo/tag.
Extract Stateless Functional Components
Let’s start with the easy and obvious when refactoring. React is about components, so let’s separate our components. We’ll use Stateless Functional Components, as they are easy to read.
We can create SearchInput.js
as follows:
The essence of React is a UI /View framework and that is what we now have in this component.
There are only two imported elements: React (requirement for JSX) and the TextField
from Material UI — no MobX, no MuiThemeProvider
, no Colors, etc.
Event handling is delegated to the handlers (given as parameters), with the exception of watching for the Enter key press. But this is an implementation concern of the input box and should be encapsulated in this component. (For example, a different UI widget library might include submit-on-enter-key behavior.)
Continuing our refactoring, we can create SearchResults.js
.
Similar to SearchInput.js
, this Stateless Functional Component is simple and only has two imports. Following Separation of Concerns (and SRP), the component for the individual search result is a parameter, ListItem
.
We could have created a Higher Order Component to wrap
ListItem
. But since we are currently using Stateless Functional Components, we will defer using HOC. (Incidentally, we will refactorSearchResults.js
to a HOC later.)
For the individual search result, we’ll create ColorListItem.js
And now, we need to refactor App.js
.
Extract Higher Order Components
For readability, we’ll rename App.js
to SearchBox.js
. For its refactoring, we have a few options.
- Let
SearchBox
passColorListItem
toSearchResults
(as a prop). - Have
index.js
passColorListItem
toSearchBox
, which would pass it toSearchResults
- Convert to
SearchBox
to a Higher Order Component (HOC)
(1) would look like this:
There is nothing wrong with this. It would be a logical consequence of extracting SearchInput.js
and SearchResults.js
. But it binds SearchBox
to ColorListItem
(bold text above) and, thus, violates Separation of Concerns. (It also limits the reuse of SearchResults
.)
(2) fixes these concerns.
(I renamed the property colors
to searchStore
, to make the concept and reusability clearer.)
But if we look at its usage, we have to pass ColorListItem
as a prop. In index.js
, we would have,
Compare it to the following:
This is the index.js
if (3), a HOC is used. The distinction is subtle, but important. ColorListItem
includes ColorSearchBox
, encapsulating the specific search result component it uses.
(The searchStore
, Colors
, is a prop. There should be one instance in the application, while there can be multiple instances of a given component, namely ColorSearchBox
.)
So, we can make SearchBox.js
a HOC as follows.
Notice that SearchBox.js
looks more like the pseduo-code in the earlier section, Seeing Duplication. We’ll further refine it a bit later.
Refactoring React Native Components
We can refactor the mobile application and extract components, following the previous pattern. I won’t go through all the details, such as extracting the SearchInput
. But they are in the README for the GitHub repo branch.
Instead, I’ll focus on the refactoring to a common SearchBox
, which our web (React) and mobile (React Native) applications will both use.
Extracting a Shared Component for Web and Mobile
For clarity, I’ve renamed SearchInput.js
, SearchResults.js
and SearchBox.js
to WebSearchInput.js
, WebSearchResults.js
and WebSearchBox.js
, respectively.
Let’s look at (Web)SearchBox.js
Lines 2–10, 19, 20, 26, 27 are specific to React.
MuiThemeProvider
, a container for Material UI components, is the only direct dependency on Material UI. But there are implicit dependencies via SearchInput
and SearchResult
. We can separate these dependencies by introducing a SearchFrame
component. It will encapsulate the MuiThemeProvider
and render SearchInput
and SearchResults
as children. We can then create a new SearchBox
HOC. It will use SearchFrame
, SearchResults
and SearchInput
.
Create a new SearchBox.js
This looks just like our pseduo-code from Seeing Duplication.
Now, change the contents of WebSearchBox.js
to
WebSearchBox
(line 26) is the result of using the SearchBox
HOC.
children
is a special React prop. In our case, it allowsWebSearchFrame
to include/renderWebSearchInput
andWebSearchResults
, which are parameters provided bySearchBox
. More about the children prop can be found here.
We will also to change WebSearchResults
to a HOC. It should encapsulate the ListItem
as part of the HOC composition.
We now have our set of reusable components. (Here is the GitHub repo/branch. Note, some directories were renamed for clarity.)
The Payoff: Reusing the Components
We’ll create GitHub repository search apps. (GitHub allows for API use without an API key, which is convenient for this tutorial.)
I’ll skip the bootstrapping details, but here is a summary
- create-react-app for the web app,
react-native init
for the mobile app - add these modules: MobX, Material UI (web only), qs (for query string encoding)…details in
package.json
in the respective projects (web and mobile)
The bulk of the effort is writing a new search store. Instead of colors, it searches GitHub repositories via the GitHub API. We can create github.js
(It’s unit test is here.)
We’ll copy some common files, for simplicity. The GitHub repo uses webpack to copy the files, as a slight convenience improvement. Sharing files/modules across Javascript projects is commonly done with NPM or Bower. (You can pay for private module registries.) You can also use Git submodules, though they can be clunky. Since our focus is component reuse and not module publishing, we’ll do the hacky thing and copy files.)
The rest is easy. Delete App.js
(and App.test.js
) and replace the contents of index.js
with
If you npm start
the github-web app, you should see
(You can also go to https://github-repo-search-box.firebaseapp.com for a live version.)
React Native: GitHub mobile app
Copy github.js
and the MobileSearch*.js
files, then create GitHubMobileSearchBox.js
and change index.ios.js
contents to
Two files, new mobile app. react-native run-ios
We may have worked hard to refactor, but reusing the components to build two new apps was easy.
Review and Wrap-Up
Let’s look at a diagram of our components:
It’s nice to see the refactoring pay off. We could focus on the specific domain logic for the new applications. We only had to define the GitHub API client and how to render repository results. The rest comes for “free”.
Furthermore, our components don’t have to deal with async issues. For example, they don’t know anything about the asynchronous fetch calls in github.js
. This is one of the wonderful benefits of our refactoring approach and how we leveraged Stateless Functional Components. Promises and asynchronous programming only occur in the place it needs to, github.js
, where the fetch
call is made.
It will be easier to extract components and reuse them, after you apply these techniques a few times. You may even write a reusable component at the start of a new view element, as the patterns become a norm in your coding.
Also, I encourage to look into libraries like recompose, which make it easier to write HOCs.
Take a look at the final GitHub repo and let me know how it goes with own refactoring of reusable components.
Please ♡ this post and follow me for updates, on Medium, or on twitter (csepulv).
Hacker Noon is how hackers start their afternoons. We’re a part of the @AMI family. We are now accepting submissions and happy to discuss advertising & sponsorship opportunities.
If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!