Sharing Code between React and React Native Apps part 1

Sharing code between React JS and React Native is an interesting idea. Writing in one language and
running it both on a web and as a native app. But because React Native doesn't use HTML to render the
app, and provides special components, it can't be done just out of the box.

Through many hours of research I have find a way to reuse much of the code. The main part is based on the fact that
React Native has a platform specific way of loading modules. In official documentation under

Platform-specific extensions it's not written that React Native detects .native extensions which are
used for both iOS and Android. Knowing that, I will use it to my advantage.

Example app

In this article we will create a simple app that will not have any logic just Title and a Lorem Ipsum text that will run both as a web app and an android/ios native app. We will start with generating a bare React Native app with:

  1. $ react-native init ReactCodeReuse
  2. $ cd ReactCodeReuse/

Once in ReactCodeReuse/ folder we create the index.web.js file, that will be the entry point for the web app, the public/ directory which will be our web folder and the app/ folder. The main app code will be in the newly created app/ folder, there we'll normally put all our components, reducers, actions etc.

The next thing is to install all of the dependencies. For that I will use the yarn package manager:

  1. $ yarn add react-dom babel-loader babel-core gulp gulp-webserver webpack webpack-stream

I won't get into what an individual package is, though through the article we'll talk about some of the packages and what they are used for.

Now that the dependencies are installed we can proceed with setting up the web part, so when we begin to code the app components, we'll see them in the browser. We'll start with creating webpack.config.js, a config file for the Webpack package manager, which is an open-source JavaScript module bundler. Webpack takes modules with dependencies and generates static assets representing those modules In other words it will take all our code with all it's dependencies like the ones we installed and will pack it in a single .js file that is going to be used in our web app as all the logic for displaying, functionality etc.

  1. module.exports = {
  2.     entry: "./index.web.js",
  3.     output: {
  4.         filename: "public/js/script.js",
  5.         sourceMapFilename: "public/js/script.map"
  6.     },
  7.     devtool: '#source-map',
  8.     module: {
  9.         loaders: [
  10.             {
  11.                 loader: 'babel-loader',
  12.                 exclude: /node_modules/
  13.             },
  14.         ]
  15.     }
  16. };

In the code above we specify what's our app's entry point (index.web.js), and where output is to be created (in folder public/js). And also the name of the output .js file (script.js.). We are using babel-loader. This package allows transpiling JavaScript files using Babel and Webpack.

With that done next thing we must create gulpfile.js which is a config file for gulp.js. Gulp is a task execution tool based on the Node.js and the Node Package Manager (npm) package management system. It is used to automate tasks that are often repeated and time-
consuming.

  1. const gulp = require('gulp');
  2. const webserver = require('gulp-webserver');
  3. const del = require('del');
  4. const webpack = require('webpack-stream');
  5. const webpackConfig = require('./webpack.config.js');
  6.  
  7. gulp.task('clean:build', function() {
  8. del('./public/js/*')
  9. });
  10.  
  11. gulp.task('build', ['clean:build'], function() {
  12.     gulp.src('./index.web.js')
  13.         .pipe(webpack(webpackConfig))
  14.         .on('error', function handleError() {
  15.             this.emit('end'); // Recover from errors
  16.         })
  17.         .pipe(gulp.dest('./'));
  18. });
  19.  
  20. gulp.task('watch', function() {
  21.     gulp.watch(['./app/**/**/**/*', './public/css/**/*', './index.web.js'], ['build']);
  22. });
  23.  
  24. gulp.task('serve', ['watch'], function() {
  25.     gulp.src('public')
  26. pipe(webserver({
  27.             fallback: 'index.html',
  28.             livereload: true,
  29.             directoryListing: false,
  30.             open: true
  31.         }));
  32. });

The explanation of the above code is that first we loaded all necessary modules, and then we created the tasks that would help us in development.

The task "clean:build" deletes our public/js/ folder, where our script.js the “brain” of out web app is residing. The task "build" starts by calling the "clean:build" task, then initiates the building process of our script.js file.
The "watch" task continuously looks if there are any changes in all sub folders of our app folder, changes on index.web.js, and changes in public/css/ folder. If there are some changes, it calls the "build" task to rebuild script.js file.

The "serve" task first calls the "watch" task, and starts the web server in our public/ folder. The web server's starting point is the Index.html file. Livereload is true so we don't have to reload the app every time we make changes.

In public/ folder we need to create the index.html file and a css/ folder with a style.css file within. Index.html is going to be a simple .html file:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3.     <head>
  4.         <meta charset="UTF-8">
  5.         <title>React Code Reuse</title>
  6.         <link rel="stylesheet" href="<a href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  7. ">https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  8. </a>        <link rel="stylesheet" href="css/styles.css">
  9.     </head>
  10.     <body>
  11.         <div id="root"></div>
  12.         <script src="js/script.js"></script>
  13.     </body>
  14. </html>


The important parts of this .html are the script tag with call of our script.js which is the bundled app, style.js file which is going to be our stylesheet for the app, and div with id=”root”. For good measure we added bootstrap css as well.

Now that the configuration is out the way, we can proceed with app creation. First, we create the folder components/ in the app/ folder. Inside the components/ folder we create the folder appscreen/. This is where we are going to put our DOM code for web and native versions. Return back to the root folder of the app and populate the index.web.js:

  1. import React from 'react';
  2. import ReactDOM from 'react-dom';
  3. import App from './app/components/appscreen';
  4. ReactDOM.render(<App />, document.getElementById('root'));

In here we created entry point for our web app by Reactjs standards. Here we are importing App component which is our root component of app, within this element all the other components, functionality etc. are contained and this all will be loaded in element with id=”root”, which is our div inside public/index.html.
We must do the same thing for native app entry point but by React Native standard, so in index.js file:

  1. import { AppRegistry } from 'react-native';
  2. import App from './app/components/appscreen';
  3. AppRegistry.registerComponent('ReactCodeReuse', () => App);

When in import component like we did index.web.js and in index.js

import App from './app/components/appscreen';

  1. import React from 'react';
  2. import screenView from './screenView';
  3. export default class screenContainer extends React.Component {
  4.     render() {
  5.         return (
  6.             <screenView/>
  7.         )
  8.     }
  9. }

In this code snippet we are calling the screenView file from the same folder as index.js. Previous knowledge that React Native is calling the file with a .native in it's name helps us, because when that component comes up for rendering it will be from file screenView.native.js, and when the same component comes up for rendering in web it will call screenView.js because React js doesn't recognizes .native in name.

We will now populate screenViews files
screenView.native.js

  1. import React, { Component } from 'react';
  2. export default class screen extends React.Component {
  3.   render() {
  4.     return (
  5.         <div className="container">
  6.             <div>
  7.                 <p className="title">header Text</p>
  8.             </div>
  9.             <div>
  10.                 <p>
  11.                     Lorem Ipsum …
  12.                </p>
  13.             </div>
  14.         </div>
  15.     );
  16.   }
  17. }
  18. screenView.native.js
  19. import React, { Component } from 'react';
  20. import { View, Text, StyleSheet } from 'react-native';
  21.  
  22. export default class Screen extends React.Component {
  23.     render() {
  24.         return (
  25.             <View style={styles.container}>
  26.                 <View>
  27.                     <Text style={styles.title}>header Text</Text>
  28.                 </View>
  29.                 <View>
  30.                     <Text>
  31.                         Lorem Ipsum …
  32.                     </Text>
  33.                 </View>
  34.             </View>
  35.         )
  36.     }
  37. }
  38.  
  39. const styles = StyleSheet.create({
  40.     container: {
  41.         flex: 1,
  42.         alignItems: 'center',
  43.         backgroundColor: '#add8e6',
  44.      },
  45.     title: {
  46.         fontSize: 32,
  47.         textAlign: 'center'
  48.         color: '#FFFFFF',
  49.         fontWeight: 'bold',
  50.         marginBottom: 20
  51.     },
  52. })

Last thing we must do is apply those styles to the web app, so in public/css/style.css add some css for styling the view.

Public/css/style.css

  1. body,
  2. html{
  3.     height: 100%;
  4.     width: 100%;
  5. }
  6. #root{
  7.     height: 100%;
  8. }
  9. .container{
  10.     height: 100%;
  11.     vertical-align: middle;
  12.     text-align: center;
  13.     background: #add8e6;
  14. }
  15. .title{
  16.     font-size: 32px;
  17.     text-align: center;
  18.     color: #FFFFFF;
  19.     font-weight: bold;
  20.     margin-bottom: 20px;
  21. }

With coding out of the way the only thing left is to start both apps, and we are done.

  1. React js: gulp serve
  2. React Native: react-native run-android / run-ios

Conclusion

This is just the bigging of what this approach can accomplished. In the next article I will move on to adding logic to this app, and add redux to the mix. This is where true power of this approach will be seen, where we'll create one logic, and use it in both web and the native app.

Boris Žmaher

back to top