Skip to content

Instantly share code, notes, and snippets.

@hmps
Last active February 23, 2018 08:39
Show Gist options
  • Save hmps/d835f3aaeac60a14381ce1411c977d9d to your computer and use it in GitHub Desktop.
Save hmps/d835f3aaeac60a14381ce1411c977d9d to your computer and use it in GitHub Desktop.
Sharing components between React web and Native - Render Prop

Render prop

TOC

Sharing components

Our current setup with napps and wapp is a bit of a hassle when you have to work on feature that are cross-platform, with plenty of code duplication. Some of that duplication is unavoidable, but some of it could be removed with a strucutre where we are able to share more code between the platforms.

As long as we don't adopt a abstraction layer for markup (which we've tried and dismissed), views won't be shareable. In React language, we won't be able to share render methods with platform specific tags. There are also some feature that are tied to the platform, e.g. event listener, interaction handlers etc. Business logic on the other hand, could easily be shared.

Files

React Native’s Metro bundler has capabilities that are well-suited for what we need. It supports loading files with two different naming patterns:

  • file.ios|android|native.js
  • file.js

When a file is referenced, the bundler will first look for files with ios|andriod|native at that path, and if that is not found it will continue to just .jsfiles. We could use this to separate the files of a component

components/
  |- Foo.js
  |- Foo.native.js

In this example, Foo.js would be for tha wapp, and Foo.native.js for napps. Since we have more control of the bundling of files for the web project we could also use a naming convention with a .web suffix for web files.

Folder structure

We could create a component folder at the root of src/, where all shared components would be placed. This would make it easier to remember to work on them both for changes that are cross-plattform.

src/
  |- common/
  |- components/
  |- native/
  |- web/

This does make the naming of the common folder a little odd though, since components are also a common resource with this approach. So a better structure might be

src/
  |- common/
    |- config/
    |- components/
    |- state/
  |- native/
  |- web/

or, simpler

src/
  |- config/
  |- components/
  |- native/
  |- state/
  |- web/

Examples

Trying to structure my thoughts on this matter I created a simple example (based on our <Experiment /> component) and implemented it in four different ways. Have a look.

import * as React from 'react';
import Experiment from './Experiment';
class Foo extends Component {
render() {
return (
<Experiment>
{variant => (
<div>
{variant === 0 && (
<h1>Base</h1>
)}
{variant === 1 && (
<h1>experiment</h1>
)}
</div>
)}
<Experiment />
);
}
}
import * as React from 'react';
import { Text } from 'react-native';
import Experiment from './Experiment';
class Foo extends Component {
render() {
return (
<Experiment>
{variant => (
<View>
{variant === 0 && (
<Text>Base</Text>
)}
{variant === 1 && (
<Text>experiment</Text>
)}
</View>
)}
<Experiment />
);
}
}
import * as React from 'react';
class Experiment extends React.Component {
// Methods are implementation details
trackExperimentSeen = () => {};
getVariant = () => {};
render() {
const { children, render: renderProp } = this.props;
this.trackExperimentSeen();
const variant = this.getVariant();
return renderProp(variant, { name });
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment