Sharing code between React Native & React
Sharing code between React Native & React

Sharing code between the web and native was one of the most interesting React Native questions since its appearance. It wasn’t the original goal of the React Native but when all our code base is written in one language it’s a natural desire to remove code duplication and reuse as much code as possible.

The approach I’m going to explain is based on the fact that React Native loads platform specific Javascript modules based on their extensions. In the official documentation it is called platform-specific extensions:

It also detects .native extensions which can be used when iOS and Android code looks the same.

So let’s see how to use this knowledge to create a cross-platform application which reuses as much code as possible.

Example application

To explain the approach I’m going to use a simple example application with one scene (page) which contains a title, ‘About’ button and ‘Help’ button. I’m going to put code examples here in the article but you can also checkout the sources.

How to start?

It is easier to begin with a React Native skeleton generated by react-native init command. It will create entry points for iOS and Android applications. In the generated project we need to create an index.web.js file which will be an entry point for the web application and the web directory to contain the web specific files: HTML, CSS, javascript, assets, etc. All our application code will reside in the app directory.

Assuming that the root component of our application will be called App, our index.web.js will look like this:

index.ios.js and index.android.js in our example will look the same:

Now let’s go through the different component types and see what their implementation could be.

Simple components with no logic

Components with no logic are basically views. Since the web and native use different components for UI, we don’t have much choice except providing separate views for each platform.

In our example application, we have the Title component which only displays a formatted title.

The following web specific code goes into TitleView.js:

iOS code goes into TitleView.ios.js:

And Android code goes intoTitleView.android.js:

Then, in the package index.js, we import the view and export it back to the outer world. React Native will import the correct module based on its extension. In this way the implementation details will be hidden from the component user, which is good.

If we run the app we’ll see different title text for the web and iOS:

Titles are different

You can check the component sources here.

Components with logic

For components which contain some internal logic, it is very natural to use presentational and container components approach. We put the logic, which should be common for all platforms, into the container component and provide different views for each platform, like we did previously for simple components with no logic.

In our example, you can see that this approach was used for the Appcomponent.

However, I’m going to show a little bit more complicated case when we have platform specific code in a container. In our example, it’s the About Buttoncomponent.

We have to process onClick event differently for the web and native. The simple solution to the problem is to use an abstract class.

So we are going to have an abstract container AbstractAboutButtonContainer.js:

Container for the web AboutButtonContainer.js:

Container for native AboutButtonContainer.native.js:

Web view AboutButtonView.js:

Native view AboutButtonView.native.js (note that we providing only one view for both iOS and Android platforms):

As earlier, in the component index.js we import the container and export it back:

If we run the app and click the ‘About’ button we’ll see two different platform specific popups with the about information:

Platform specific popups

You can see the component sources here.

Another possible solution for this case is to introduce the alert.js module which will handle cross-platform functionality internally. But I wanted to show this specific approach with components and it’s not easy to come up with ideal synthetic examples.

Components connected to the Redux store

In order to avoid code duplication in connected components, we need to connect them to Redux store in the component index.js.

Let’s have a look at the Help Button component which displays a number of help requests made during one application session. We need to provide it with a number of previous ‘Help’ button clicks which will be stored in the Redux store. Also, we need to pass it an action creator to dispatch the HELP_BUTTON_CLICKEDaction and increase the stored value.

We’ll use a similar approach as we just used for the components with logic. The only difference will be in the index.js file which will contain the following code:

Instead of simply importing and exporting the container we wrap it in the root HelpButton component which we connect to Redux.

Then the same as earlier we are going to have an abstract container AbstractHelpButtonContainer.js:

Container for the web HelpButtonContainer.js:

Container for native HelpButtonContainer.native.js:

Web view AboutButtonView.js:

And native view AboutButtonView.native.js:

If we run the app and click the help button two times we’ll see that number of clicks is counted:

Number of clicks is counted

You can see the component sources here.

What’s next?

We analyzed three types of components and specified how to share code between different platforms for each type.

In real world applications, we will also have actions, reducers, routers, utilities and other services. Most of them will be cross-platform which doesn’t require any actions from us. We import and use them as if we were writing code only for one platform.

For services, which have different implementations depending on the platform, we can use the same solution as we did for components. We create a directory with the web implementation service.js, native implementation service.native.js and simply import and export the service back in the index.js file.

Cross platform module directory structure

Some services or components will be present only on particular platforms. In such case, I put them into platform specific subdirectories: app/web or app/native, where only platform related code resides.

Conclusion

The approach is very simple and is very easy to use. Instead of having several platform specific applications we have only one cross-platform application. Separating modules based on their functionality instead of a platform makes it very convenient to develop all platforms in parallel.

The example app is very simplified. It is not following all the best React/React Native practices and shouldn’t be used as an example of well-designed React application. The goal was to explain the approach easy for understanding, providing many examples but still keeping it a short read. Some not topic related things weren’t explained at all. Some possible complications of given solutions weren’t provided (e.g. using of abstract views) but that’s something you can figure out yourself based on your particular situation. If you have any questions please check the sources or ask me in the comments.

Recommended Courses: 

Learn to Build Mobile Apps With React Native

The Complete React Native and Redux Course

React Native : Build Native Mobile Applications

React Native: Build Your Own Mobile Apps

via : http://ihor.burlachenko.com/code-sharing-between-react-and-react-native-applications/

LEAVE A REPLY

Please enter your comment!
Please enter your name here