Write once run anywhere with sharing components
Write once, run anywhere
Ever wonder writing only one piece of code but run it across all platforms (web + native)? Yes, you definitely can!
Start from Native with react-ative
- Initiate a react native project with react-native-cli
react-native rnweb
A project with the following directory structure will be created:
// App.js code
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
android:
'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});type Props = {};
export default class App extends Component<Props> {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}>To get started, edit App.js</Text>
<Text style={styles.instructions}>{instructions}</Text>
</View>
);
}
}const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});// index.js code
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';AppRegistry.registerComponent(appName, () => App);
You can checkout the source code at react-native branch of the repo
- Run IOS
react-native run-ios
- Run Android
react-native run-android
Make it work for Web
Ideally we would like our react native project to work on web as well, with minimum code modification. The goal can be achieved with the help of react-native-web.
Principle
In the react native code, we import modules like AppRegistry and Text from react-native package. These modules aren’t recognized by web browsers and thus won’t work if used directly on web platform. react-native-web implements the same protocols/APIs in web terminology used in react-native. By aliasing react-native to react-native-web in webpack config, we are then able to use the equivalent web components without code change.
//webpack.config.js
resolve: {
extensions: [".js", ".jsx"],
alias: {
"react-native": "react-native-web"
}
},
Packaging with the above webpack config with the following code
import {Text} from 'react-native'
=> actally imports Text component from ‘react-native-web’.
Let’s take a look into the source code of Text component implementation in react-native-web. In the render function the return is either a div or span element which works on web platform.
// Text/index.js
import applyLayout from '../../modules/applyLayout';
import applyNativeMethods from '../../modules/applyNativeMethods';
import { bool } from 'prop-types';
import { Component } from 'react';
import createElement from '../createElement';
import StyleSheet from '../StyleSheet';
import TextPropTypes from './TextPropTypes';class Text extends Component<*> {
//... render() {
const {
dir,
numberOfLines,
onPress,
selectable,
style,
/* eslint-disable */
adjustsFontSizeToFit,
allowFontScaling,
ellipsizeMode,
lineBreakMode,
minimumFontScale,
onLayout,
onLongPress,
pressRetentionOffset,
selectionColor,
suppressHighlighting,
textBreakStrategy,
tvParallaxProperties,
/* eslint-enable */
...otherProps
} = this.props; //... const component = isInAParentText ? 'span' : 'div'; return createElement(component, otherProps);
} // ...
}
// ...export default applyLayout(applyNativeMethods(Text));
react native bundling doesn’t go through webpack while web packaging does. Thus the aliasing in webpack config only works for web packaging.
Steps Details
- Add web folder in paralell with android and ios
- Add index.html under web folder
<!DOCTYPE html>
<html>
<head>
<title>React Native Web</title>
<meta name="viewport" content="width=device-width">
</head>
<body>
<div id="root"></div>
</body>
</html>
- Install dependency
npm install --save react-art react-dom react-native-web
html-webpack-plugin webpack-dev-server webpack webpack-cli
babel-loader @babel/preset-env @babel/preset-react
- Add webpack config file: webpack.config.js
const HtmlWebPackPlugin = require("html-webpack-plugin");module.exports = {
entry: {
app: "./index.js"
},
resolve: {
extensions: [".js", ".jsx"],
alias: {
"react-native": "react-native-web"
}
},
output: {
filename: "bundle.js"
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env", "@babel/preset-react"]
}
}
}
]
},
plugins: [
new HtmlWebPackPlugin({
template: "./web/index.html",
filename: "./index.html"
})
],
devServer: {
historyApiFallback: true
}
};
- Add quick start command in scripts attribute in package.json
{
web: "webpack-dev-server"
}
- Update App.js
// App.js
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View} from 'react-native';const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
android:
'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
web: 'Cmd+R or F12 to reload'
});type Props = {};
export default class App extends Component<Props> {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Text style={styles.instructions}>To get started, edit App.js</Text>
<Text style={styles.instructions}>{instructions}</Text>
</View>
);
}
}const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
- Update index.js
//index.js
import {AppRegistry, Platform } from 'react-native';
import App from './App';
import {name as appName} from './app.json';AppRegistry.registerComponent(appName, () => App);
if (Platform.OS === 'web') {
AppRegistry.runApplication(appName, {
rootTag: document.getElementById("root")
});
}
- Run
npm run web
Reference
Notice
- If you want to follow the latest news/articles for the series of my blogs, Please 「Watch」to Subscribe.