React Native on the Web

Twitter Lite

React Native

... on the Web 🤔

HTML

HTML is too Difficult

Difficult => Complex

Complexity Factor 

  • UIに意味をもたせる難しさ
    • 状態とUI
    • レスポンシブなデザイン
  • セマンティックなHTMLとは
    • <div>, <section>, <article>, ...
    • <strong>, <b>, <em>, <i>, ...

Native App UI meets Web

WebとNative Appの境目はなくなりつつある

Components from Native App to Web

  • Pull to Refresh
  • Highlight on Tap
  • Gesture (Swipe, Long Press, Force Touch)
  • Offline
  • and other UIs ...

対応するためのTips

  • flexbox, grid layout
  • reset.css
  • aタグよりbuttonタグ
  • :active :hoverの考え方
  • -webkit-overflow-scrolling

解決のためのライブラリ

  • Onsen UI
  • Ionic
  • Ratchet
  • Semantic-ui
  • jQuery mobile

React Native

React Native

  • JavaScriptでiOS, Android アプリを作れる
  • JSはブリッジで各ネイティブコンポーネントを呼び出す
import React, { Component } from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';

export default class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Button
          onPress={onPressLearnMore}
          title="Learn More"
          color="#841584"
          accessibilityLabel="Learn more about this purple button"
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  }
});
import React, { Component } from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';

export default class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Button
          onPress={onPressLearnMore}
          title="Learn More"
          color="#841584"
          accessibilityLabel="Learn more about this purple button"
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  }
});

<View>はレイアウトに関することのみ

import React, { Component } from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';

export default class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Button
          onPress={onPressLearnMore}
          title="Learn More"
          color="#841584"
          accessibilityLabel="Learn more about this purple button"
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  }
});

<Text>はテキストに関することのみ

代表的なコンポーネント

<View>

<Text>

<TextInput>

<Button>

<Image>

<Picker>

<Slider>

<Switch>

<WebView>

<ScrollView>

<ListView>

代表的なコンポーネント

<View>

<Text>

<TextInput>

<Button>

<Image>

<Picker>

<Slider>

<Switch>

<WebView>

<ScrollView>

<ListView>

<div>, <section>, <artcle>

<span>, <h1>, <h2>,<p>

<input>, <textarea>

<a>, <button>

<img>

<select>, <option>

render() {
  return (
    <ListView
      refreshControl={
        <RefreshControl
          refreshing={this.state.refreshing}
          onRefresh={this._onRefresh.bind(this)}
        />
      }
      ...
    >
      ...
    </ListView>
  );
}

Pull to Refresh

render() {
  return (
    <View>
      <TouchableOpacity
       style={styles.button}
       onPress={this.onPress}
      >
        <Text> Touch Here </Text>
      </TouchableOpacity>

      <TouchableHighlight
       style={styles.button}
       onPress={this.onPressImage}
      >
        <Image source={{url:...}} />
      </TouchableHighlight>
    </View>
  )
}

feedback on Touch

render() {
  return (
    <View>
      <ActivityIndicator size="large" color="#0000ff" />
    </View>
  )
}

ActivityIndicator

render() {
  return (
    <View>
      <Text
        numberOfLines={2}
      > Lorem Ipsum is simply dummy text of the printing and typesetting
 industry. Lorem Ipsum has been the industry's 
standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five ce
nturies, but also the leap into electronic typesetting, remaining essentially unchanged.  
      </Text>
    </View>
  )
}

numberOfLines

render() {
  return (
    <View>
      <View style={{ marginBottom: 100 }}>
        <Text>margin bottom 100</Text>
      </View>
      <View style={{ marginTop: 100 }}>
        <Text>margin top 100</Text>
      </View>
    </View>
  )
}

no margin conflict

React Native のコンポーネント

  • UIコンポーネントを抽象化
    • 必要な(低レベルな)UIコンポーネントが揃っている
  • コンポーネントの責務が明確化
    • テキストを扱うためにspan, p,などタグに迷わない
  • 考えることが減って実装がシンプルに

React Native Web

React Native for Web

React Native for Web

React DOMでReact NativeコンポーネントやAPIを実行することができる。ベースはReact.js

  • できること
    • 簡単で高速にWeb UIを作成
    • ネイティブのインタラクション
    • タッチ・マウス・キーボード入力サポート
    • ベンダプレフィックスサポート
    • RTLレイアウト、アクセシビリティ
    • React Dev Tool
  • Write once, render anywhere
    • 書いたコードはReact Nativeでも大半は利用可能
    • Node.jsを用いてSSRも可能

React Native for Web

実績

React Native Dom

React Native Dom

React NativeのコードをWebに展開。ベースはReact Native

  • モバイル上のReact Nativeと同じアーキテクチャを利用
    • ReactコンポーネントのロジックはWeb Workerで実行されメインスレットはレンダリングのみ行う
  • React Native on mobileと同じレイアウト
    • YogaへのカスタムバインディングとWebアセンブリにコンパイルを利用することにより、ネイティブとウェブで同じレイアウトを実現
  • Metro Bundlerを利用
  • DOM APIにも対応
  • こっちこそWrite once, render anywhere

React Native Dom

ただしまだExperimental😜

まとめ

  • 低レイヤーコンポーネントの責務の明確化
    • React Nativeのコンポーネントは簡単でシンプルな新たなhtmlといえる
       
  • Write once, render anywhereの実現
    • Web/iOS/Androidが混在するプロジェクトが多い昨今、React Native Webは選択肢としてありなのでは?

 

END

deck

By Kyohei Rampage Tsukuda