SHARING CODE BETWEEN

REACT AND REaCT-NATIVE

THE GOOD, THE BAD & THE UNSHAREABLE

bene@theodo.co.uk
Ben Ellerby

Types of Code

UI Render

Business Logic

Configuration

API / Formatting

DIFFERENT RENDER ENVIRONMENT

  • Different render environments:

 

<div> != <View>

<ul> != <FlatList>

UI Render

ui rENDER coDE

UI Render

  • React-Native-Web (no longer supported by Twitter and SSR issues)
  • ReactXP (SSR issues and have not seen a project using it successfully)
  • styled-components/universal (Same SSR issues as React-Native-Web and too experimental)

Styled-components

UI Render

Visual primitives for the component age.

const Button = styled.button`
  border-radius: 3px;
  padding: 0.25em 1em;
  margin: 0 1em;
  background: transparent;
  color: palevioletred;
  border: 2px solid palevioletred;
`;

Styled-components

/universal

UI Render

Styled-components

/universal

UI Render

Builds off React-Primitives

"Primitive React Interfaces Across Targets"

  • Animated
  • StyleSheet
  • View
  • Text
  • Image
  • Touchable
  • Easing
  • Dimensions
  • PixelRatio
  • Platform:(iOS, Android, Web, Sketch, VR,...)
  • TextInput

https://github.com/lelandrichardson/react-primitives

Styled-components

/universal

UI Render

Styled-components

/universal

UI Render

Sketch

React

React-Native

Styled-components

/universal

UI Render

Note that this is an experimental release: There might be bugs and there also isn’t a massive amount of documentation

Styled-components

/universal

UI Render

ui rENDER coDE

UI Render

I believe that Web, Mobile Web and Native Application environments all require a specific design and user experience.

It’s worth noting that we’re not chasing “write once, run anywhere.” Different platforms have different looks, feels, and capabilities, and as such, we should still be developing discrete apps for each platform, but the same set of engineers should be able to build applications for whatever platform they choose, without needing to learn a fundamentally different set of technologies for each. We call this approach “learn once, write anywhere.”

Types of Code

UI Render

Business Logic

Configuration

API / Formatting

Types of Code

API / Formatting

API calls, authentication and formatting of request & response data.

 

 

 

If we make a POST to a REST API on web, it's the same POST on native.

Types of Code

Config files, translation files and most constant data is not render environment specific.

 

If I update a translation on my app, it's likely I want that changed rolled out on my site too.

Configuration

Types of Code

Shared Config Object

Native Config Object

{

    ....,

    foo: "bar",

}

Web Config Object

Configuration

Types of Code

Again environment independent.

 

If a user can only add 20 items to their basket, this rule will hold true in web and native equally.

Business Logic

Types of Code

UI Render

Business Logic

Configuration

API / Formatting

State Management & Data

+

State Management & Data

+

  • sagas
  • translations
  • analytic event firing...
  • product data
  • search
  • pagination...

State Management & Data

UI

Render Environment Independent

Our whole redux store is shared

Our Apollo queries are shared 

Higher Order Components

HigerOrderComponent

WrappedComponent

props: { foo: "bar" }

props.foo //bar

Higher Order Components: Apollo

import React from 'react';
import { Query } from 'react-apollo';

const withData = (query) => WrappedComponent => {
  return props => (
    <Query query={query}>
      {injectedProps => 
            <WrappedComponent {...injectedProps} {...props} />};
    </Query>
  );
};

export default withData;

Higher Order Components: Apollo

All Apollo query HOCs are shared:

 

e.g. withSearchResults, withBasket, withProducts

Conditional Rendering

renderIfAuthorised
import React from 'react';
import { connect } from 'react-redux';
import { compose, branch, renderComponent } from 'recompose';
import { getUserPermissionSets } from 'login.selectors';

const mapStateToProps = state => ({
  userPermissions: getUserPermissionSets(state),
});

export default (permission, UnauthorisedComponent) =>
  compose(
    connect(mapStateToProps),
    branch(
      ({ userPermissions }) =>
         !userPermissions.includes(permission),
         renderComponent(UnauthorisedComponent),
    ),
  );

APPROACH To Sharing

MadeNative

MadeWeb

MadeShared

APPROACH To Sharing

MadeShared

   /hoc
      /queries
      /conditional-rendering
   /config
      /translations
      /colours
   /types
   /api
   /redux
      /actions
      /reducers
      /selectors
      /sagas

APPROACH To Sharing

var path = require('path');
module.exports = {
  entry: './shared/index.js',
  output: {
    path: path.resolve(__dirname),
    filename: 'index.js',
    libraryTarget: 'commonjs2',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        include: path.resolve(__dirname, 'shared'),
        exclude: /(node_modules|bower_components|build)\//,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['env'],
          },
        },
      },
    ],
  },
  externals: {
    react: 'commonjs react',
    'react-apollo': 'commonjs react-apollo',
  },
};

Webpack config to transpile to commonjs2

MadeShared

APPROACH To Sharing

...
  "dependencies": {
    ...
    "made-shared":
      "git+https://AUTHTOKEN:x-oauth-basic@github.com/REPO/made-shared.git#master-1.05",
    ...
...
  • Using basic x-auth tokens of a 'bot' user with read-only access
  • Using git tags as versions (#BRANCH-VERSION)
  • `yarn link` for local dev

APPROACH To Sharing

...
  "dependencies": {
    ...
    "made-shared": "1.5.1",
    ...
...

Staying Generic...

Staying Generic...

 

4 Weeks with one change to the shared library!

App Center!

MadeNative

We use AppCenter as our App CI

 

It needs to pull the shared library, but it's private...

App Center!

Me: Hello, we’re wanting to have a shared library used in our project. This would require an npm install from a private NPM repo (through package cloud). What is the best practice for adding a private npm access on the AppCenter CI?

MadeNative

App Center!

Microsoft: We currently only support cloud git repositories hosted on VSTS, Bitbucket and GitHub. Support for private repos is not available yet but we are building new features all the time, you can keep an eye out on our roadmap for upcoming features.

MadeNative

App Center!

MadeNative

Conclusion

  • Code sharing is a great advantage of React & React-Native
  • Share your non-design render code.
  • Your app and web app should have a different UX
  • HOCs help share component logic
  • Publishing a private npm package creates a good workflow

Conclusion

  • Multi Repo approach can help the team build a mental model of code sharing

ben@ellerby.tech

https://www.linkedin.com/in/benjaminellerby/

16:9 React Amsterdam 2019: Sharing Code Between React and React-Native: What Not to Share

By Ben Ellerby

16:9 React Amsterdam 2019: Sharing Code Between React and React-Native: What Not to Share

Talk given at the React-Native-London meetup.

  • 757