Share Code between React and React Native Apps

39,420 reads

@ csepulv Christian Sepulveda espresso fanatic, coder (still), VP Engineering Bastille, …yeah, espresso comes first.

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.

reactions

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.

reactions

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:

reactions

UX consistency, both within an application and across devicesMake cross-cutting upgrades: improve a component and update all its uses easilyreuse routing and authorization rulesSwitch libraries (for example, the apps below uses MobX for state management, but Redux could be swapped in)

reactions

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.

reactions

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 Component(Final GitHub repo) You can see the result and how easy it is to create additional applications with the reused components.

reactions

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.

reactions

Cory House has a good introduction here.

reactions

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.

reactions

const AutocompleteTextBox = makeAutocomplete(TextBox);<br> export AutocompleteTextBox;

//…later

import {AutoCompleteTextBox} from ‘./somefile’;

The Facebook documentation is here. franleplant has a detailed post as well.

reactions

We’ll use both HOC and Stateless Functional Components in a few moments.

reactions

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.

reactions

It will be a one screen application. It will not use routes nor multiple scenes as the focus is on component reuse.

reactions

We will add a second pair of applications (React and React Native), which will reuse the components we extract.

reactions

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:

reactions

create-react-app bootstraps the React applicationI use Material UI for the UI elements in the React/web appreact-native init bootstraps the React Native applicationI use MobX for state management. (Michel Weststrate, creator of Mobx, has good tutorials here and here.)

reactions

https://colors-search-box.firebaseapp.com/ is a running demo of the web version. Screenshots of both are below (web, then mobile):

reactions

Refactoring To Reuse

Refactoring to ReuseAchieving Code Reuse is About Perspective

reactions

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.

reactions

I’ve achieved the sustained reuse by applying a few guidelines: Separation of Concerns, Single Responsibility Principle, and the removal of duplication.

reactions

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.

reactions

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.

reactions

Applying these ideas is like moving puzzle pieces around, to find where they meet and what patterns they reveal.

reactions

Let’s start by looking for duplication.

reactions

Seeing Duplication

The web and mobile applications have two main components.

In the web application, App.js

reactions

import React, {Component} from 'react' ; import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider' ; import {TextField, List, ListItem, Divider} from 'material-ui' import {observer} from 'mobx-react' ; // (Make material-ui happy) // Needed for onTouchTap // http://stackoverflow.com/a/34015469/988941 import injectTapEventPlugin from 'react-tap-event-plugin' ; injectTapEventPlugin(); export default observer( class App extends Component { static propTypes = { colors : React.PropTypes.object.isRequired }; handleKeyDown = ( event ) => { const ENTER_KEY = 13 ; if (event.keyCode === ENTER_KEY) { event.preventDefault(); this .props.colors.search(); } }; handleQueryChange = ( event, value ) => { this .props.colors.updateQuery(value); }; render() { const listItems = this .props.colors.results.map( ( color, index ) => { return ( < div key = { ` color-div- ${ index }`}> <ListItem key={`color-${index}`} primaryText={color.name} style={{backgroundColor: color.hex} }/> <Divider key={`divider-${index}`}/> </div> ); }); return ( <MuiThemeProvider> <div> <TextField hintText="Search..." floatingLabelFixed={true} fullWidth={true} value={this.props.colors.query} onChange={this.handleQueryChange} onKeyDown={this.handleKeyDown}/> <List> {listItems} </List> </div> </MuiThemeProvider> ); } });

reactions

In the mobile application,

SearchView.js

reactions

import React, {Component} from 'react' ; import { StyleSheet, View, TextInput, ListView, Text } from 'react-native' ; import {observer} from 'mobx-react/native' ; const styles = StyleSheet.create({ container : { flex : 1 , backgroundColor : '#eeeeee' , marginTop : 25 }, row : { flexDirection : 'row' , justifyContent : 'center' , padding : 10 }, text : { flex : 1 , }, separator : { flex : 1 , height : 1 , backgroundColor : '#8E8E8E' , } }); const dataSource = new ListView.DataSource({ rowHasChanged : ( r1, r2 ) => r1.name !== r2.name}); export default observer( class SearchView extends Component { static propTypes = { colors : React.PropTypes.object.isRequired }; renderRow = ( rowData, sectionId, rowID ) => { return ( < View style = {[styles.row, { backgroundColor: rowData.hex }]}> <Text style={styles.text}>{rowData.name}</Text> </ View > ); }; render() { return ( < View style = {styles.container} > <TextInput style={{height: 40, paddingLeft: 8, borderColor: 'gray', borderWidth: 1}} onChangeText={(text) => this.props.colors.updateQuery(text)} value={this.props.colors.query} returnKeyType={'search'} onSubmitEditing={() => this.props.colors.search()} /> <ListView enableEmptySections={true} dataSource={ dataSource.cloneWithRows(this.props.colors.results.slice())} renderRow={this.renderRow} renderSeparator={(sectionId, rowId) => <View key={rowId} style={styles.separator}/>} /> </View> ); } });

The following outlines their structure.

reactions

Almost the same, but the platform differences between React and React Native are in the way.

reactions

The two components have similar structures. Ideally they would share components and look something like this:

reactions

Our goal: a common, shared set of components

reactions

And in pseudo-code,

reactions

export default observer( class extends Component { static propTypes = { colors : React.PropTypes.object.isRequired }; //pseudo code renderSearchResult() { return ( < SearchResult style = {{backgroundColor: color.hex }}> {color.name} </ SearchResult > ) } render() { return ( < Container > <SearchInput value={colors.query} onSubmit={colors.search} onTextChange={colors.updateQuery} /> <SearchResults/> </Container> ) } }

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.

reactions

Separation of Concerns and Single Responsibility

Both

App.js

SearchView.js

reactions

UI implementation: e.g. separate ListItem and ListView from the concept of search results

and 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

andmix domain logic (our app logic) with the platform implementation and library integrations. We can improve the design if we isolate

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.

reactions

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.

reactions

We can create

SearchInput.js

reactions

import React from 'react' ; import {TextField} from 'material-ui' ; const SearchInput = ( {query, onSubmit, onQueryUpdate} ) => { const handleKeyDown = ( event ) => { const ENTER_KEY = 13 ; if (event.keyCode === ENTER_KEY) { event.preventDefault(); onSubmit(); } }; return ( < TextField hintText = "Search..." floatingLabelFixed = {true} fullWidth = {true} value = {query} onChange = {(event, value ) => onQueryUpdate(value)} onKeyDown={handleKeyDown}/> ); }; SearchInput.propTypes = { query: React.PropTypes.string.isRequired, onSubmit: React.PropTypes.func.isRequired, onQueryUpdate: React.PropTypes.func.isRequired }; export default SearchInput; view rawSearchInput.js-1 hosted with ❤ by GitHub

as follows:

The essence of React is a UI /View framework and that is what we now have in this component.

reactions

There are only two imported elements: React (requirement for JSX) and the

TextField

MuiThemeProvider

reactions

from Material UI — no MobX, no, 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.)

reactions

Continuing our refactoring, we can create

SearchResults.js.

reactions

import React from 'react' ; import {TextField} from 'material-ui' ; const SearchInput = ( {query, onSubmit, onQueryUpdate} ) => { const handleKeyDown = ( event ) => { const ENTER_KEY = 13 ; if (event.keyCode === ENTER_KEY) { event.preventDefault(); onSubmit(); } }; return ( < TextField hintText = "Search..." floatingLabelFixed = {true} fullWidth = {true} value = {query} onChange = {(event, value ) => onQueryUpdate(value)} onKeyDown={handleKeyDown}/> ); }; SearchInput.propTypes = { query: React.PropTypes.string.isRequired, onSubmit: React.PropTypes.func.isRequired, onQueryUpdate: React.PropTypes.func.isRequired }; export default SearchInput; view rawSearchInput.js-1 hosted with ❤ by GitHub

Similar to

SearchInput.js

reactions

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 refactor SearchResults.js to a HOC later.)

, 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.

For the individual search result, we’ll create

ColorListItem.js

reactions

import React from 'react' ; import {ListItem, Divider} from 'material-ui' const ColorListItem = ( {result} ) => { return ( < div > <ListItem primaryText={result.name} style={{backgroundColor: result.hex} }/> <Divider/> </div> ); }; ColorListItem.propTypes = { result: React.PropTypes.object.isRequired }; export default ColorListItem; view rawColorListItem.js-1 hosted with ❤ by GitHub

And now, we need to refactor

App.js.

reactions

Extract Higher Order Components

For readability, we’ll rename

App.js

SearchBox.js.

reactions

Let SearchBox pass ColorListItem to SearchResults (as a prop). Have index.js pass ColorListItem to SearchBox , which would pass it to SearchResults Convert to SearchBox to a Higher Order Component (HOC)

toFor its refactoring, we have a few options.

(1) would look like this:

reactions

import React, {Component} from ‘react’; import MuiThemeProvider from ‘material-ui/styles/MuiThemeProvider’; import {observer} from ‘mobx-react’; // (Make material-ui happy) // Needed for onTouchTap // http://stackoverflow.com/a/34015469/988941 import injectTapEventPlugin from ‘react-tap-event-plugin’; injectTapEventPlugin(); import SearchInput from ‘./SearchInput’; import SearchResults from ‘./SearchResults’; import ColorListItem from ‘./ColorListItem’; export default observer( class SearchBox extends Component { static propTypes = { colors : React.PropTypes.object.isRequired }; render() { return ( < MuiThemeProvider > <div> <SearchInput query={this.props.colors.query} onQueryUpdate={value => this.props.colors.updateQuery(value)} onSubmit={() => this.props.colors.search()} /> <SearchResults ListItem={ColorListItem} results={this.props.colors.results.slice()}/> </div> </MuiThemeProvider> ); } });

There is nothing wrong with this. It would be a logical consequence of extracting

SearchInput.js

SearchResults.js

SearchBox

ColorListItem

SearchResults

reactions

and. But it bindstoand, thus, violates Separation of Concerns. (It also limits the reuse of.)

(2) fixes these concerns.

reactions

import React, {Component} from ‘react’; import MuiThemeProvider from ‘material-ui/styles/MuiThemeProvider’; import {observer} from ‘mobx-react’; // (Make material-ui happy) // Needed for onTouchTap // http://stackoverflow.com/a/34015469/988941 import injectTapEventPlugin from ‘react-tap-event-plugin’; injectTapEventPlugin(); import SearchInput from ‘./SearchInput’; import SearchResults from ‘./SearchResults '; export default observer(class SearchBox extends Component { static propTypes = { searchStore: React.PropTypes.object.isRequired, ListItem: React.PropTypes.func.isRequired }; render() { return ( <MuiThemeProvider> <div> <SearchInput query={this.props.searchStore.query} onQueryUpdate={value => this.props.searchStore.updateQuery(value)} onSubmit={() => this.props.searchStore.search()} /> <SearchResults ListItem={ListItem} results={this.props.searchStore.results.slice()}/> </div> </MuiThemeProvider> ); } });

(I renamed the property

colors

searchStore

reactions

to, to make the concept and reusability clearer.)

But if we look at its usage, we have to pass

ColorListItem

index.js

reactions

import React from ‘react’; import ReactDOM from ‘react-dom’; import SearchBox from ‘./SearchBox’; import ‘./index.css’; import Colors from ‘./colors’ ReactDOM.render( < SearchBox searchStore = {new Colors ()} ListItem = {ColorListItem} /> , document.getElementById(‘root’) );

as a prop. In, we would have,

Compare it to the following:

reactions

import React from ‘react’; import ReactDOM from ‘react-dom’; import SearchBox from ‘./SearchBox’; import ‘./index.css’; import Colors from ‘./colors’ import ColorListItem from ‘./ColorListItem’; const ColorSearchBox = SearchBox(ColorListItem); ReactDOM.render( < ColorSearchBox searchStore = {new Colors ()}/> , document.getElementById(‘root’) );

This is the

index.js

ColorListItem

ColorSearchBox

reactions

if (3), a HOC is used. The distinction is subtle, but important.includes, 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.)

reactions

So, we can make

SearchBox.js

reactions

import React, {Component} from 'react' ; import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider' ; import {observer} from 'mobx-react' ; // (Make material-ui happy) // Needed for onTouchTap // http://stackoverflow.com/a/34015469/988941 import injectTapEventPlugin from 'react-tap-event-plugin' ; injectTapEventPlugin(); import SearchInput from './SearchInput' ; import SearchResults from './SearchResults' ; const SearchBox = ( ListItem ) => { return observer( class extends Component { static propTypes = { searchStore : React.PropTypes.object.isRequired }; render() { return ( < MuiThemeProvider > <div> <SearchInput query={this.props.searchStore.query} onQueryUpdate={value => this.props.searchStore.updateQuery(value)} onSubmit={() => this.props.searchStore.search()} /> <SearchResults ListItem={ListItem} results={this.props.searchStore.results.slice()}/> </div> </MuiThemeProvider> ); } }); };

a HOC as follows.

Notice that

SearchBox.js

reactions

Refactoring React Native Components

looks more like the pseduo-code in the earlier section, Seeing Duplication . We’ll further refine it a bit later.

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.

reactions

Instead, I’ll focus on the refactoring to a common

SearchBox

reactions

Extracting a Shared Component for Web and Mobile

, which our web (React) and mobile (React Native) applications will both use.

For clarity, I’ve renamed

SearchInput.js

SearchResults.js

SearchBox.js

WebSearchInput.js

WebSearchResults.js

WebSearchBox.js

reactions

andtoand, respectively.

Let’s look at (Web)

SearchBox.js

reactions

import React, {Component} from 'react' ; import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider' ; import {observer} from 'mobx-react' ; // (Make material-ui happy) // Needed for onTouchTap // http://stackoverflow.com/a/34015469/988941 import injectTapEventPlugin from 'react-tap-event-plugin' ; injectTapEventPlugin(); import SearchInput from './SearchInput' ; import SearchResults from './SearchResults' ; const SearchBox = ( ListItem ) => { return observer( class extends Component { static propTypes = { searchStore : React.PropTypes.object.isRequired }; render() { return ( < MuiThemeProvider > <div> <SearchInput query={this.props.searchStore.query} onQueryUpdate={value => this.props.searchStore.updateQuery(value)} onSubmit={() => this.props.searchStore.search()} /> <SearchResults ListItem={ListItem} results={this.props.searchStore.results.slice()}/> </div> </MuiThemeProvider> ); } }); };

Lines 2–10, 19, 20, 26, 27 are specific to React.

reactions

MuiThemeProvider

SearchInput

SearchResult

SearchFrame

MuiThemeProvider

SearchInput

SearchResults

SearchBox

SearchFrame

SearchResults

SearchInput

reactions

, a container for Material UI components, is the only direct dependency on Material UI. But there are implicit dependencies viaand. We can separate these dependencies by introducing acomponent. It will encapsulate theand renderandas children. We can then create a newHOC. It will useand

Create a new

SearchBox.js

reactions

import React, {Component} from 'react' ; const SearchBox = ( SearchFrame, SearchInput, SearchResults ) => { return class extends Component { static propTypes = { searchStore : React.PropTypes.object.isRequired }; render() { return ( < SearchFrame > <SearchInput query={this.props.searchStore.query} onQueryUpdate={value => this.props.searchStore.updateQuery(value)} onSubmit={() => this.props.searchStore.search()} /> <SearchResults results={this.props.searchStore.results.slice()}/> </SearchFrame> ); } }; }; export default SearchBox

reactions

This looks just like our pseduo-code from Seeing Duplication.

reactions

Now, change the contents of

WebSearchBox.js

reactions

import React from 'react' ; import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider' ; // (Make material-ui happy) // Needed for onTouchTap // http://stackoverflow.com/a/34015469/988941 import injectTapEventPlugin from 'react-tap-event-plugin' ; injectTapEventPlugin(); import WebSearchInput from './WebSearchInput' ; import WebSearchResults from './WebSearchResults' ; import SearchBox from './SearchBox' import {observer} from 'mobx-react' ; const WebSearchFrame = ( {children} ) => { return ( < MuiThemeProvider > <div> {children} </div> </ MuiThemeProvider > ); }; const WebSearchBox = ListItem => observer(SearchBox(WebSearchFrame, WebSearchInput, WebSearchResults(ListItem))); export default WebSearchBox;

to

WebSearchBox (line 26) is the result of using the SearchBox HOC.

reactions

children

WebSearchFrame

WebSearchInput

WebSearchResults

SearchBox

reactions

is a special React prop. In our case, it allowsto include/renderand, which are parameters provided by. More about the children prop can be found here

We will also to change

WebSearchResults

ListItem

reactions

import React, {Component} from 'react' ; import {List} from 'material-ui' const WebSearchResults = ListItem => { return class extends Component { static propTypes = { results : React.PropTypes.array.isRequired }; render() { const listItems = this .props.results.map( ( result, index ) => { return ( < ListItem key = { ` result- ${ index }`} result = {result}/ > ); }); return ( <List> {listItems} </List> ); }; }; }; export default WebSearchResults;

to a HOC. It should encapsulate theas 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.)

reactions

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.)

reactions

I’ll skip the bootstrapping details, but here is a summary

reactions

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

reactions

import {extendObservable, runInAction} from 'mobx' ; import qs from 'qs' ; export default class GitHub { constructor () { extendObservable( this , { results : [], query : '' }); } search() { const self = this ; return new Promise ( ( resolve, reject ) => { const encodedQuery = qs.stringify({ q : this .query}); fetch( `https://api.github.com/search/repositories? ${encodedQuery} ` ) .then( ( response ) => { if (response.ok) return response.json(); else reject( ` ${response.status} : ${response.statusText} ` ); }) .then( ( json ) => { runInAction( () => { self.results = json.items; resolve(); }); }) .catch( ( error ) => { runInAction( () => { self.results = []; reject(error); }); }); }); } updateQuery(value) { runInAction( () => this .query = value); } }

reactions

(It’s unit test is here.)

reactions

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.)

reactions

The rest is easy. Delete

App.js

App.test.js

index.js

reactions

import React from 'react' ; import ReactDOM from 'react-dom' ; import WebSearchBox from './WebSearchBox' ; import './index.css' ; import {ListItem, Divider, Avatar} from 'material-ui' import GitHub from './github' const RepoListItem = ( {result} ) => { return ( < div > <ListItem primaryText={result.name} leftAvatar={<Avatar src={result.owner.avatar_url}/>}/> <Divider/> </div> ); }; RepoListItem.propTypes = { result: React.PropTypes.object.isRequired }; const RepoSearchBox = WebSearchBox(RepoListItem); ReactDOM.render( <RepoSearchBox searchStore={new GitHub()}/>, document.getElementById('root') );

(and) and replace the contents ofwith

If you

npm start the github-web

reactions

app, you should see

reactions

(You can also go to https://github-repo-search-box.firebaseapp.com for a live version.)

reactions

React Native: GitHub mobile app

Copy

github.js

MobileSearch*.js

GitHubMobileSearchBox.js

reactions

import React from 'react' ; import { StyleSheet, View, TextInput, ListView, Text, Image } from 'react-native' ; import MobileSearchBox from './MobileSearchBox' ; const styles = StyleSheet.create({ row : { flexDirection : 'row' , justifyContent : 'center' , padding : 10 }, thumb : { width : 48 , height : 48 , }, text : { flex : 1 , } }); const RepoListItem = ( {result} )=> { return ( < View style = {[styles.row]} > <Image style={styles.thumb} source={{url:result.owner.avatar_url}} /> <Text style={styles.text}>{result.name}</Text> </View> ) }; const GitHubSearchBox = MobileSearchBox(RepoListItem); export default GitHubSearchBox;

and thefiles, then create

and change

index.ios.js

reactions

/** * Sample React Native App * https://github.com/facebook/react-native * @flow */ import React, {Component} from 'react' ; import {AppRegistry} from 'react-native' ; import GitHub from './github' ; import GitHubMobileSearchBox from './GitHubMobileSearchBox' ; export default class App extends Component { render() { return ( < GitHubMobileSearchBox searchStore = {new GitHub ()}/> ); } } AppRegistry.registerComponent('GitHubMobile', () => App);

contents to

Two files, new mobile app. react-native run-ios

reactions

We may have worked hard to refactor, but reusing the components to build two new apps was easy.

reactions

Review and Wrap-Up

Let’s look at a diagram of our components:

reactions

(box icon is from thenounproject.com, courtesy of Tinashe Mugayi)

reactions

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”.

reactions

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.

reactions

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.

reactions

Also, I encourage to look into libraries like recompose, which make it easier to write HOCs.

reactions

Take a look at the final GitHub repo and let me know how it goes with own refactoring of reusable components.

reactions

Tags