avatarBenoit VALLON

Summary

The website presents a project demonstrating how to create a mobile, desktop, and web application using a single codebase, leveraging React-native, NW, Electron, and React.

Abstract

The project discussed on the website is an innovative approach to application development that utilizes a shared codebase across multiple platforms. The author, inspired by the release of React-native, explores the possibility of unifying the code for iOS and Android mobile apps, desktop applications (using NW and Electron), and web applications (using React). The code is publicly available on GitHub in the repository named react-native-nw-react-calculator. The project is not just a proof of concept but is structured to mimic a real-world production environment, adopting the Flux architecture and tools like Grunt and Webpack for efficient builds and hot reloading. The author emphasizes the reusability of code, particularly in the Flux actions/stores, and the strategic structuring of components to maximize shared logic while accommodating platform-specific differences. The project's success is evident in its ability to run as a beautiful calculator app with a memory feature across all platforms, based on Robert O’Dowd's design.

Opinions

  • The author believes in the efficiency of using a single codebase for multiple platforms, highlighting the significant reuse of code.
  • The project is seen as a practical example of cross-platform development, using tools and architectures that are standard in production environments.
  • The author values the importance of a well-structured codebase, as evidenced by the careful organization of components and the use of inheritance and selective rendering for platform-specific needs.
  • The project is praised for its real-world applicability, demonstrated by the inclusion of a "memory of previous formulae" feature in the calculator app.
  • The author appreciates the community's contribution, notably the design by Robert O’Dowd and the assistance from @cjbprime, which facilitated the implementation of platform-specific rendering logic.
  • The use of file extensions to manage platform-specific code during the build process is highlighted as an effective solution within the project's architecture.

A mobile, desktop and website App with the same code

React-native, NW, Electron and React, all in one

A few months ago after react-native was released I started to wonder how it would be possible to do a mobile App, desktop App and website App with the same code base. I knew it was possible but I wanted to explore how to do it and what would be the final volume of reused code. So I created this project.

Get the code on Github

Of course, all of the code is available on Github under this repository react-native-nw-react-calculator.

More about the project

This project shows how the source code can be architectured to run on multiple devices. As of now, it is able to run as:

It is a beautiful calculator with the “memory of previous formulae” feature based on the design of Robert O’Dowd who kindly authorized me the use it. The original design made by Robert was part of his project called “Simplifycation” visible here.

The iOS App and Android App

Like a production project

I wanted for this project to use all the tools that I would have used in a production project, so it is based on the flux architecture and uses tools like grunt and webpack to create the builds and for the hot reloading feature.

The desktop App running on NW on the left and Electron on the right

In the code

3 main builds

Although we are going to run on 4 devices (iOS, Android, desktop, web), we only need to create 3 builds. Entry files for those builds are at the root of the src directory.

  • index.ios.js is the entry file for the iOS App build
  • index.android.js is the entry file for the Android App build
  • index.js is the entry file for the website App and desktop App builds (both with NW and Electron)

The build for the website App and the desktop Apps is the same because it is possible to reuse the exact same code in both environments. Then, this build is simply called from different .html sources.

Flux architecture actions/stores

All the flux architecture is share at 100% to all the different builds. This means that all the logic and data management code is done once and reuse everywhere.

This allows us to have an easy tests suite and to ensure that our code is working properly on all the devices.

The web App

Components

Here comes the real interest of the project. Finding how the components can be structured to share most of their logic but still allowing for some specificity for each device was not so easy.

Having inheritance for the shared logic and only redefine what is specific to every device sounds the right solution but how to include only the code that we want for each build. It could have been done with webpack and its variables evaluated during the build but thanks to @cjbprime I managed to do it based on the file extensions.

Basically, every component has a main Class which inherits a base Class containing all the logic. Then, the main component import a different Render function which has been selected during the build. The file extension .ios.js, .android.js or .js is used by the build tool to import only the right file.

The .native.js files contain code that is shared between both mobile platforms (iOS & Android). Currently, the .ios.js and .android.js files compose this .native.js file since all code is shared right now. However, if a component needed to be different for platform specific reasons, that code would be included in the corresponding platform specific files.

At the end, every component is defined by 6 files. If we look at the screen component, here is its structure.

Screen.js
├── ScreenBase.js
├── ScreenRender.ios.js (specific to iOS build
├── ScreenRender.android.js (specific to Android build)
├── ScreenRender.native.js (shared mobile app code - iOS & Android)
└── ScreenRender.js (used during Website and Desktop build)

And here is the main Class file (Screen.js) which composes the other files.

'use strict';
import Base from './ScreenBase';
import Render from './ScreenRender';
export default class Screen extends Base {
  constructor (props) {
    super(props);
  }
  render () {
    return Render.call(this, this.props, this.state);
  }
}

As the last code sample, here is the ScreenRender.ios.js file which imports the ScreenRender.native.js file.

'use strict';
import Render from ‘./ScreenRender.native’;
export default function () {
  return Render.call(this, this.props, this.state);
}

Originally published at blog.benoitvallon.com.

JavaScript
React
React Native
Recommended from ReadMedium