Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Crossing platforms with JavaScript & React
Search
Robert DeLuca
February 07, 2017
Technology
150
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Crossing platforms with JavaScript & React
Robert DeLuca
February 07, 2017
More Decks by Robert DeLuca
See All by Robert DeLuca
Testing Big in JavaScript (Bleeding edge web)
robdel12
0
270
Testing Big in JavaScript
robdel12
0
880
Testing your app with VoiceOver
robdel12
0
92
Getting started with ember-cli-deprecation-workflow
robdel12
0
480
Thumper
robdel12
1
140
Other Decks in Technology
See All in Technology
AIで社員の自主発信に広報目線を組み込む
_mossann_t
0
130
AIエージェントの権限管理 3: Agentic RAG の Fine grained access control 編
ren8k
0
140
Slack上でインフラをトラブルシュートする! Agentic Platform Engineeringの第一歩
teru0x1
5
1.8k
サーバーレスをどこまで使う? WebRTC対戦ゲームで選んだVPSとの共存設計
kaidouji85
0
170
SREは、MCPとAutopilotをこう使え!
kazumax55
3
910
エージェントはローカル、検証はMicroVM — Lambda MicroVMsでつくるServerless CI
fujioka6789
3
460
JSONataとAWS Step Functionsで目指すRuntimelessな世界
mu7889yoon
0
270
ADKで始める業務改善 - AIエージェント開発時の考えと設計
harappa80
2
240
Vibe Coding で作ったプロダクトをどう安全に動かすか / How to Safely Run Products Built with Vibe Coding
glidenote
0
270
事業課題から技術的負債に向き合う
sansantech
PRO
2
2k
2026-09-18 gotanda.sre Terraformで複数環境作ったり、複数Stateに分割したりそれとTerragrunt / Terraform multi envs and multi states
masasuzu
3
590
越境するなら専門用語を使うな高校校歌 / If you wanna cross border, you shouldn't use jargon
vtryo
0
150
Featured
See All Featured
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
660
GitHub's CSS Performance
jonrohan
1033
470k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
Site-Speed That Sticks
csswizardry
13
1.5k
Color Theory Basics | Prateek | Gurzu
gurzu
1
470
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.9k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.8k
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
870
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Transcript
Crossing platforms with JavaScript & React
@robdel12
JavaScript is EVERYWHERE
Web
Server
iOS
Android
Desktop
None
What is cross platform JS?
JS that can run on more than one platform
None
“Why did the iOS team implement it like this?”
“The Android app currently doesn’t support that”
https://twitter.com/dan_abramov/status/812047645732651009
Easier to share code across many teams
More team collaboration since there’s more overlap
It allows teams to own products & not be separated
by technology
TL;DR your team now owns the iOS, Android, and (maybe)
web apps.
Consistency is
Cheaper
If you can build iOS & Android apps in the
same code base it should be cheaper
Why not bet on the web?
Native will be better than mobile web for a while
Why not take the web tooling & get native results?
You are betting on the web
Can’t beat them, join them
I decided to be ambitious
Build an Instagram clone for Web, iOS, & Android
Why an Instagram clone?
Use Impagination.js to power an Infinite scroll of images
None
Impagination will work on any JS codebase
Building infinite scroll in React Native with Impagination http://bit.ly/reactnativeinfinitescroll
We’ve already used Impagination in four different platforms
What else can be shared?
Experiment time
None
Three phases to the experiment • Planning • Implementation •
Postmortem
Planning
None
The stack • React Native • React (DOM) • Auth0
(authentication) • Graph.cool (backend) • Impagination (infinite datasets)
What should the app do? • Login / Sign up
• See your profile & images you’ve posted • Edit your profile • Post a new photo • Main list feed showing everyones posts
Web demo
Implementation
What’s the approach?
Build the web app
Start to build the native app
Realize I’ve already solved these problems in the web app
Refactor
ListPage.js
ListPage.js handles both UI & data right now
ListPage for native duplicates a lot form web ListPage
class ListPage extends React.Component { static propTypes = { data:
React.PropTypes.object, } state = { dataset: null, datasetState: null, } setupImpagination() {} componentWillMount() {this.setupImpagination();} setCurrentReadOffset = (event) => {} render () { return ( <div style={{maxWidth: "600px", margin: "0 auto", padding: "20px 0"}}> <Infinite elementHeight={ITEM_HEIGHT} handleScroll={this.setCurrentReadOffset} useWindowAsScrollContainer> {this.state.datasetState.map(record => { if (record.isPending && !record.isSettled) { return <LoadingPost key={Math.random()} />; } return <Photo key={record.content.id} photo={record.content} user={record.content.user} />; })} </Infinite> </div> ); } } const FeedQuery = gql`query($skip: Int!, $first: Int!) { allPhotos(orderBy: createdAt_DESC, first: $first, skip: $skip) { } }`; export default graphql(FeedQuery, {options: {variables: { skip: 0, first: PAGE_SIZE }}})(ListPage); Web ListPage.js
` class ListPage extends React.Component { static propTypes = {
data: React.PropTypes.object, } state = { dataset: null, datasetState: null, } setupImpagination() {} componentWillMount() {this.setupImpagination();} setCurrentReadOffset = (event) => {} render () { return ( <ScrollView style={{flex: 1}} scrollEventThrottle={300} onScroll={this.setCurrentReadOffset} removeClippedSubviews={true}> {this.state.datasetState.map(record => { if(record.isPending && !record.isSettled) { return <LoadingPost key={Math.random()}/>; } return <Photo key={record.content.id} photo={record.content} user={record.content.user} />; })} </ScrollView> ); } } const FeedQuery = gql`query($skip: Int!, $first: Int!) { allPhotos(orderBy: createdAt_DESC, first: $first, skip: $skip) { } }`; export default graphql(FeedQuery, {options: {variables: { skip: 0, first: PAGE_SIZE }}})(ListPage); Native ListPage.js
Everything but the UI is the same
New structure
Presentation & container components
<IndexRoute component={ListPageContainer} /> <Route path='feed' component={ListPageContainer} />
import ListPageView from ‘../components/presentational/ListPageView’; class ListPageContainer extends React.Component { state
= { dataset: null, datasetState: null, } setupImpagination() {} componentWillMount() {this.setupImpagination();} setCurrentReadOffset = (event) => {} render () { return ( <ListPageView setCurrentReadOffset={this.setCurrentReadOffset} datasetState={this.state.datasetState} />; ); } } const FeedQuery = gql`query($skip: Int!, $first: Int!) { allPhotos(orderBy: createdAt_DESC, first: $first, skip: $skip) { } }`; export default graphql(FeedQuery, {options: {variables: { skip: 0, first: PAGE_SIZE }}})(ListPage);
Make the container component render a separate presentation component
Leave setting the readOffset to the presentation components
setCurrentReadOffset function is passed as a prop from the container
component
t import React, { Component } from 'react'; import Infinite
from 'react-infinite'; import Photo from '../presentational/Photo'; import LoadingPost from '../presentational/LoadingPost'; const ITEM_HEIGHT = 600; const HEADER_HEIGHT = 80; class ListPageView extends Component { setCurrentReadOffset = (event) => { let currentItemIndex = Math.ceil((window.scrollY - HEADER_HEIGHT) / ITEM_HEIGHT); this.props.setCurrentReadOffset(currentItemIndex); } render() { return ( <div style={{maxWidth: "600px", margin: "0 auto", padding: "20px 0"}}> <Infinite elementHeight={ITEM_HEIGHT} handleScroll={this.setCurrentReadOffset} useWindowAsScrollContainer> {this.props.datasetState.map(record => { if (record.isPending && !record.isSettled) { return <LoadingPost key={Math.random()} />; } return <Photo key={record.content.id} photo={record.content} user={record.content.user} />; })} </Infinite> </div> ); } } export default ListPageView; Web presentation component
Native presentation component import React, { Component } from 'react';
import Photo from '../presentational/Photo'; import LoadingPost from '../presentational/LoadingPost'; import { ScrollView } from 'react-native'; const ITEM_HEIGHT = 485; class ListPageView extends Component { setCurrentReadOffset = (event) => { let currentOffset = Math.floor(event.nativeEvent.contentOffset.y); let currentItemIndex = Math.ceil(currentOffset / ITEM_HEIGHT); this.props.setCurrentReadOffset(currentItemIndex); } render() { return ( <ScrollView style={{flex: 1}} scrollEventThrottle={300} onScroll={this.setCurrentReadOffset} removeClippedSubviews={true}> {this.props.datasetState.map(record => { if(record.isPending && !record.isSettled) { return <LoadingPost key={Math.random()}/>; } return <Photo key={record.content.id} photo={record.content} user={record.content.user} />; })} </ScrollView> ); } } export default ListPageView;
This theme continues throughout the entire app
<IndexRoute component={ListPageContainer} /> <Route path='feed' component={ListPageContainer} /> <Route path='new' component={CreatePostContainer}
onEnter={this.requireAuth.bind(this)} /> <Route path='signup' component={CreateUserContainer} /> <Route path='profile' component={UserProfileContainer} > <IndexRoute component={UserProfileContainer} /> <Route path='edit' component={EditProfileContainer} /> </Route> <Route path='logout' component={() => <Logout logout={this.handleToken.bind(this)} /> } />
Native app demo
Postmortem
Building the apps in time was hard…
None
None
Figuring out what code is shareable
Figuring out how to make that code shareable
React Router is neat & works cross platform There are
different imports for React Native & React
None
Auth0 was very easy to implement on both platforms. There
are different APIs for React Native & React
AsyncStorage vs localStorage
What all ended up being shared?
✅ List feed ✅ User profile ✅ Edit user profile
✅ Sign up ✅ New post
Beyond login mostly everything else is the same
The UI changed but not the business logic
Key takeaways
We’re in a post DOM world
Write JavaScript interaction models
The UI framework will change but the underlying model driving
it won’t
“It’s just JavaScript”
We get stronger libraries by increasing the number of users
& contributors.
React makes this very easy thanks to React & React
Native
I was able to share the same five container components
across three different platforms
Write one container component and many UI components
The core of this app is shared
That’s a cost savings
I own this entire product & its 3 platforms
In 2 weeks I was able do all of this
Cross platform JS FTW
Instagram also agrees with me https://engineering.instagram.com/react-native-at-instagram- dd828a9a90c7#.i364vchox
None
If this kind of stuff interests you
We’re hiring!
Thanks! @robdel12