React Native: Cross Platform Code Reuse

Mike Shi
4 min readDec 25, 2016

One of the great advantages to using React Native is that your mobile code can be mostly written in JavaScript. From that, you can minimize a lot of duplicate code by simply sharing logic across web and mobile versions of an application. I’ll go over my current folder setup and how I separate my code to isolate platform specific code from shared reusable code.

My application uses Redux to handle data/logic and I’m currently writing it for both web and mobile. I assume that the mobile and web application will have the same functionality (containers, actions, and reducers will be largely shared) but will have very different UI across different platforms. Therefore I’m making no attempt to share component code across platforms, only logic code.

Also keep in mind that React Native’s goal is not “write once, run anywhere” but rather “learn once, write anywhere”. The intention is to allow engineers to be more flexible and lower ramp-up time switching between code bases on multiple platforms. While code reuse is nice, it’s certainly not the goal of React Native.

Folder Setup

The first thing we’d need to do is to set up folders to hold common files. I have a pretty hacky set up where I have 3 directories, /common, /web, and /react-native. I simply have a script that checks for any file changes in /common and then copies those files into /web/common and /react-native/common. This hacky solution is needed as currently the RN packager does not allow including files outside of the root directory (though webpack would happily do it). An alternative is to keep all common js files in /react-native/common and then allow webpack to reach into /react-native to search for common files, but that solution seemed a bit messier. A more proper solution would most likely use npm link but I wasn’t too familiar with that and I’m not sure if RN packager will play nicely with it. Let me know if npm link turns out to be a good solution to this problem!

Platform Specific Code

This part is what I think is the hardest part. Examples of platform specific code that I had to deal with would involve storing data into the appropriate underlying local storage and managing push notifications on iOS. I struggled with a couple different variations of how I wanted to separate my platform specific code from my common code, to maximize reusability. My current is to isolate the platform specific logic into Redux middleware.

The platform specific component code (code that actually renders elements onto the screen) would just simply accept the same set of props across platforms so that they could share the same container.

Now how does this look like all together?

Components

We’d have our platform specific component code in /react-native/exampleComponent.js Nothing new.

Containers

We would then have our container code in /common/exampleContainer.js which would then be copied into/react-native/common/exampleContainer.js. It can then import the appropriate component by simply using a relative import like import ExampleComponent from '../exampleComponent. Keeping the folder structure identical across all platforms make this possible and relatively simple.

Actions

Since actions are shared across platform, we would have our actions just like we normally would so it would be in /common which of course would be then copied into the respective platform directories.

Middleware

This is where we’ll put platform specific logic. We would have our middleware in /react-native/middleware.js. Of course you can have common middleware as well that is shared across platforms, but we’ll focus on platform-specific code here.

Here we’ll just simply define some middleware to handle actions. For example we can export a storageMiddleware in /react-native/middleware.js that will handle saving the state of the application using a utility function specific for iOS.

import {
saveState
} from './utils/ios-storage';
export function storageMiddleware({getState}) {
return next => action => {
next(action);
if (!action.save)
return;
saveState(getState());
};
}

Now if we ever pass an action that has a save property set to true, we’ll be able to save the state onto local storage with the appropriate function call per platform.

The idea is that there could be a web middleware counterpart as well that would also be able to take appropriate action on the save property on the web application.

Entry Point

Now the easy part in the entry point ex. index.ios.js

//... import RN, Redux, and friends here.
import {storageMiddleware} from './middleware';
import ExampleContainer from './common/exampleContainer';
class App extends Component {
constructor() {
this.store = createStore(reduce, applyMiddleware(
ReduxThunk,
logger,
storageMiddleware
));
}
render() {
return (
<Provider store={this.store}>
<ExampleContainer/>
</Provider>
);
}
}
AppRegistry.registerComponent('App', () => App);

Now we have the app hooked up so that our common actions will pass through our platform specific logic storageMiddleware and use the shared container component ExampleContainer which uses the platform specific ExampleComponent.

Fin.

Thanks for sticking through all the way. Let me know in the comments if anything was confusing or unclear or anything about my style. (More importantly!) also let me know if you know of a cleaner solution to cross platform code sharing in React Native. I’d love to move to something less hacky and more conventional.

If this helped you out, help me out with a 💚 below!

--

--