react native - workshop
TRANSCRIPT
REACT NATIVE
REACT NATIVEWorkshop
Fellipe Chagas@chagasaway
Ajudamos grandes empresas e startups a focarem em seu negócio
entregando tecnologia
DESENVOLVIMENTO NATIVO
DESENVOLVIMENTO NATIVO
DESENVOLVIMENTO NATIVO
DESENVOLVIMENTO NATIVO
DESENVOLVIMENTO NATIVO
DESENVOLVIMENTO HÍBRIDO
DESENVOLVIMENTO HÍBRIDO
DESENVOLVIMENTO HÍBRIDO
DESENVOLVIMENTO HÍBRIDO
DESENVOLVIMENTO HÍBRIDO
DESENVOLVIMENTO HÍBRIDO
DESENVOLVIMENTO HÍBRIDO
DESENVOLVIMENTO HÍBRIDO
DESENVOLVIMENTO HÍBRIDO
FRAMEWORKS
FRAMEWORKS
FRAMEWORKS
FRAMEWORKS
FRAMEWORKS
FRAMEWORKS
FRAMEWORKS
35.350 stars 897 contribuidores
e crescendo…
REACT
REACT
REACT
REACT
REACT
e milhares de outros…
REACTCOMPONENTES
REACTCOMPONENTES
TUDO PODE SER JAVASCRIPT
REACT
VIRTUAL DOM
COMPONENTES
TUDO PODE SER JAVASCRIPT
COMPONENTES
APP
COMPONENTESHEADER
TABS
CARD
CARD
COMPONENTESHEADER
TABS
CARD
CARD IMAGEUSER AVATAR
CARD TEXT
CARD IMAGEUSER AVATAR
CARD TEXT
COMPONENTESHEADER
TABS
CARD
CARD IMAGEUSER AVATAR
CARD TEXT
CARD IMAGEUSER AVATAR
CARD TEXT
HEADER
TABS
ACCOUNT INFO
USER AVATAR
ACCOUNT SETTINGS
CARD TEXT
CARD IMAGEUSER
AVATAR
COMPONENTES
CARD TEXT
CARD IMAGEUSER
AVATAR
COMPONENTES
import React, { Component, StyleSheet, PropTypes, Image, } from 'react-native'
export default class Avatar extends Component { constructor(props) { super(props) }
render() { return ( <Image source={this.props.image} style={styles.image} /> ) } }
Avatar.propTypes = { image: PropTypes.object.isRequired, }
const styles = StyleSheet.create({ image: { width: 30, height: 30, borderRadius: 15 }, })
CARD IMAGEUSER
AVATAR
CARD TEXT
COMPONENTES
COMPONENTEScomponentWillMount
COMPONENTEScomponentWillMount
componentDidMount
COMPONENTEScomponentWillMount
componentDidMount
componentWillReceiveProps
COMPONENTEScomponentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
COMPONENTEScomponentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
COMPONENTEScomponentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
COMPONENTEScomponentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
COMPONENTEScomponentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
Props
COMPONENTEScomponentWillMount
componentDidMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
componentWillUnmount
Props &State
Props
REACT
VIRTUAL DOM
COMPONENTES
TUDO PODE SER JAVASCRIPT
TUDO PODE SER JAVASCRIPT
TUDO PODE SER JAVASCRIPT
HTML
import React, { Component, StyleSheet, PropTypes, Image, } from 'react-native'
export default class Avatar extends Component { constructor(props) { super(props) }
render() { return ( <Image source={this.props.image} style={styles.image} /> ) } }
Avatar.propTypes = { image: PropTypes.object.isRequired, }
const styles = StyleSheet.create({ image: { width: 30, height: 30, borderRadius: 15, }, })
import React, { Component, StyleSheet, PropTypes, Image, } from 'react-native'
export default class Avatar extends Component { constructor(props) { super(props) }
render() { return ( <Image source={this.props.image} style={styles.image} /> ) } }
Avatar.propTypes = { image: PropTypes.object.isRequired, }
const styles = StyleSheet.create({ image: { width: 30, height: 30, borderRadius: 15, }, })
TUDO PODE SER JAVASCRIPT
<Image source={this.props.image} style={styles.image}
/>
TUDO PODE SER JAVASCRIPT<Image source={this.props.image}
style={styles.image} />
JSX
TUDO PODE SER JAVASCRIPT<Image source={this.props.image}
style={styles.image} />
JSXlet image = React.createElement(
Image, { source: this.props.image,
style: styles.image })
TUDO PODE SER JAVASCRIPT
<Image source={this.props.image} style={styles.image}
/>
JSX
<img src=‘imagem.png’ style=‘border-radius: 5px’
/>
HTML
CSS
import React, { Component, StyleSheet, PropTypes, Image, } from 'react-native'
export default class Avatar extends Component { constructor(props) { super(props) }
render() { return ( <Image source={this.props.image} style={styles.image} /> ) } }
Avatar.propTypes = { image: PropTypes.object.isRequired, }
const styles = StyleSheet.create({ image: { width: 30, height: 30, borderRadius: 15, }, })
import React, { Component, StyleSheet, PropTypes, Image, } from 'react-native'
export default class Avatar extends Component { constructor(props) { super(props) }
render() { return ( <Image source={this.props.image} style={styles.image} /> ) } }
Avatar.propTypes = { image: PropTypes.object.isRequired, }
const styles = StyleSheet.create({ image: { width: 30, height: 30, borderRadius: 15, }, })
TUDO PODE SER JAVASCRIPT
StyleSheet.create({ image: { width: 30, height: 30, borderRadius: 15, }, })
TUDO PODE SER JAVASCRIPT
StyleSheet.create({ image: { width: 30, height: 30, borderRadius: 15, }, })
StyleSheet
TUDO PODE SER JAVASCRIPT
StyleSheet.create({ image: { width: 30, height: 30, borderRadius: 15, }, })
StyleSheet
.image { width: 30px,
height: 30px, borderRadius: 15px }
CSS
REACT
VIRTUAL DOM
COMPONENTES
TUDO PODE SER JAVASCRIPT
VIRTUAL DOM
HTML DOM
VIRTUAL DOMHTML DOM
JAVASCRIPT
VIRTUAL DOMHTML DOM
JAVASCRIPT
VIRTUAL DOM
VIRTUAL DOM
VIRTUAL DOM
ANDROID DOM
iOS DOM
HTML DOM
VIRTUAL DOMANDROID
DOMiOS
DOMHTML DOM
SONY DOM
SAMSUNG DOM
WINDOWS DOM
BRASTEMP DOM
LG DOM
PHILIPS DOM
REACT NATIVE
COMPONENTESView Image Text Activity
Indicator
Modal Navigator Refresh Control ScrollView
StatusBar Switch ListView TextInput
WebView MapView ProgressBar Android …
APIsAlert Animated AppState BackAndroid
CameraRoll Clipboard DatePicker Android Dimensions
Geolocation Linking NetInfo ToastAndroid
PixelRatio PushNotification IOS Vibration +++
COMUNIDADE
COMUNIDADE
COMUNIDADE
BIBLIOTECAS NATIVAS
BIBLIOTECAS NATIVASBIBLIOTECA NATIVA
REACT NATIVE
BIBLIOTECAS NATIVASBIBLIOTECA NATIVA
REACT NATIVE
BIBLIOTECAS NATIVASBIBLIOTECA NATIVA
REACT NATIVE
BIBLIOTECAS NATIVASBIBLIOTECA NATIVA
REACT NATIVE
REACT NATIVE BRIDGE
'use strict';
var {PayPal} = require('react-native').NativeModules;
var constants = {}; var constantNames = Object.keys(PayPal).filter(p => p == p.toUpperCase()); constantNames.forEach(c => constants[c] = PayPal[c]);
var functions = { paymentRequest(payPalParameters) { return new Promise(function(resolve, reject) { PayPal.paymentRequest(payPalParameters, resolve, reject); }); } };
var exported = {}; Object.assign(exported, constants, functions);
module.exports = exported;
'use strict';
var {PayPal} = require('react-native').NativeModules;
var constants = {}; var constantNames = Object.keys(PayPal).filter(p => p == p.toUpperCase()); constantNames.forEach(c => constants[c] = PayPal[c]);
var functions = { paymentRequest(payPalParameters) { return new Promise(function(resolve, reject) { PayPal.paymentRequest(payPalParameters, resolve, reject); }); } };
var exported = {}; Object.assign(exported, constants, functions);
module.exports = exported;
public class PayPal extends ReactContextBaseJavaModule { @ReactMethod public void paymentRequest( final ReadableMap payPalParameters, final Callback successCallback, final Callback errorCallback ) { final String environment = payPalParameters.getString("environment"); final String clientId = payPalParameters.getString("clientId"); final String price = payPalParameters.getString("price"); final String currency = payPalParameters.getString("currency"); final String description = payPalParameters.getString("description");
PayPalConfiguration config = new PayPalConfiguration().environment(environment).clientId(clientId);
startPayPalService(config);
PayPalPayment thingToBuy = new PayPalPayment( new BigDecimal(price), currency, description, PayPalPayment.PAYMENT_INTENT_SALE);
Intent intent = new Intent(activityContext, PaymentActivity.class) .putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config) .putExtra(PaymentActivity.EXTRA_PAYMENT, thingToBuy);
currentActivity.startActivityForResult(intent, paymentIntentRequestCode); } }
public class PayPal extends ReactContextBaseJavaModule { @ReactMethod public void paymentRequest( final ReadableMap payPalParameters, final Callback successCallback, final Callback errorCallback ) { final String environment = payPalParameters.getString("environment"); final String clientId = payPalParameters.getString("clientId"); final String price = payPalParameters.getString("price"); final String currency = payPalParameters.getString("currency"); final String description = payPalParameters.getString("description");
PayPalConfiguration config = new PayPalConfiguration().environment(environment).clientId(clientId);
startPayPalService(config);
PayPalPayment thingToBuy = new PayPalPayment( new BigDecimal(price), currency, description, PayPalPayment.PAYMENT_INTENT_SALE);
Intent intent = new Intent(activityContext, PaymentActivity.class) .putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config) .putExtra(PaymentActivity.EXTRA_PAYMENT, thingToBuy);
currentActivity.startActivityForResult(intent, paymentIntentRequestCode); } }
public class PayPal extends ReactContextBaseJavaModule { @ReactMethod public void paymentRequest( final ReadableMap payPalParameters, final Callback successCallback, final Callback errorCallback ) { final String environment = payPalParameters.getString("environment"); final String clientId = payPalParameters.getString("clientId"); final String price = payPalParameters.getString("price"); final String currency = payPalParameters.getString("currency"); final String description = payPalParameters.getString("description");
PayPalConfiguration config = new PayPalConfiguration().environment(environment).clientId(clientId);
startPayPalService(config);
PayPalPayment thingToBuy = new PayPalPayment( new BigDecimal(price), currency, description, PayPalPayment.PAYMENT_INTENT_SALE);
Intent intent = new Intent(activityContext, PaymentActivity.class) .putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config) .putExtra(PaymentActivity.EXTRA_PAYMENT, thingToBuy);
currentActivity.startActivityForResult(intent, paymentIntentRequestCode); } }
PLATAFORMAS
PLATAFORMAS
PLATAFORMAS
=
PLATAFORMAS
=
PLATAFORMAS
=
PLATAFORMASconst Loader = Platform.select({ ios: () => require('LoaderIOS'), android: () => require('LoaderAndroid'), })()
render () { ... <Loader /> ... }
PLATAFORMAS
import Loader from './Loader'
render () { ... <Loader /> ... }
- Loader.ios.js- Loader.android.js
REACT NATIVE TOOLS
REACT NATIVE TOOLS
REACT NATIVE TOOLS
Não precisa compilar
Desenvolvimento dinâmico
Monitor de performance
REACT NATIVE TOOLS
$ react-native logs-ios$ react-native logs-android
REACT NATIVE TOOLS
$ react-native logs-ios$ react-native logs-android
Debug on Chrome
CODE PUSH
CODE PUSH
CODE PUSH
CODE PUSH
npm i —save react-native-code-push@latest
CODE PUSHnpm i —save react-native-code-push@latest
import codePush from ‘react-native-code-push'
...
componentDidMount() { codePush.sync()
}
CODE PUSHnpm i —save react-native-code-push@latest
import codePush from ‘react-native-code-push'
...
componentDidMount() { codePush.sync()
}
code-push release-react SampleApp ios
CODE PUSH
code-push release-react SampleApp
-- rollout 25%
-- targetBinaryVersion “~1.1.0”
CODE PUSH
code-push rollback
RNPM
RNPM
RNPM
RNPM
RNPM
RNPM
RNPM
npm i rnpm -g
RNPMnpm i rnpm -g
npm i react-native-module --savernpm link react-native-module
RNPMnpm i rnpm -g
rnpm install react-native-module
BOAS PRÁTICAS
BOAS PRÁTICAS
ESTRUTURA DO SEU PROJETO
FLUXO DE DADOS
TESTANDO SEUS COMPONENTES
FLUXO DE DADOS
COMPONENT
FLUXO DE DADOSCOMPONENT
API
FLUXO DE DADOSCOMPONENT
API
class SampleComponent extends Component { ... componentDidMount() {
fetch(‘http://api.com/data‘).then((res) => { this.setState({ data: res })
}) } ...
}
FLUXO DE DADOSCOMPONENT
API
COMPONENT
API
COMPONENT
API
COMPONENT
API
COMPONENT
API
FLUXO DE DADOSCOMPONENT
API
COMPONENT
API
COMPONENT
API
COMPONENT
API
COMPONENT
API
FLUXO DE DADOS
FLUXO DE DADOS
REDUX
COMPONENT
API
ACTION CREATORS
REDUX
COMPONENTAPI
ACTION CREATORS REDUCERS
REDUX
COMPONENTAPI
ACTION CREATORS
STORE
REDUCERS
REDUX
COMPONENTAPI
ACTION CREATORS
STORE
REDUCERS
REDUX
COMPONENTAPI
ACTION CREATORS
componentDidMounted
STORE
REDUCERS
REDUX
COMPONENTAPI
ACTION CREATORS
componentDidMounted
fetchData STORE
REDUCERS
REDUX
COMPONENTAPI
ACTION CREATORS
componentDidMounted
fetchDataHTTP STORE
REDUCERS
REDUX
COMPONENTAPI
ACTION CREATORS
componentDidMounted
fetchDataHTTP JSON STORE
REDUCERS
REDUX
COMPONENTAPI
ACTION CREATORS
STORE
REDUCERS
componentDidMounted
fetchDataHTTP JSON
Data
REDUX
COMPONENTAPI
ACTION CREATORS
STORE
REDUCERS
componentDidMounted
fetchDataHTTP JSON
Data
New State
REDUX
COMPONENTAPI
ACTION CREATORS
STORE
REDUCERS
componentDidMounted
fetchDataHTTP JSON
Data
New State
State
BOAS PRÁTICAS
ESTRUTURA DO SEU PROJETO
FLUXO DE DADOS
TESTANDO SEUS COMPONENTES
TESTANDO SEUS COMPONENTES
TESTANDO SEUS COMPONENTES
TESTANDO SEUS COMPONENTES
TESTANDO SEUS COMPONENTES
COMPONENTACTION CREATORS STOREREDUCERS
TESTANDO SEUS COMPONENTES
COMPONENTACTION CREATORS STOREREDUCERS
TESTANDO SEUS COMPONENTES
COMPONENTACTION CREATORS STOREREDUCERS
TESTANDO SEUS COMPONENTES
COMPONENTACTION CREATORS REDUCERS
TESTANDO SEUS COMPONENTES
ACTION CREATORS
describe('actions', () => { it('should create an action to load items', () => {
// given const loading = true const expectedAction = {
type: 'START_LOADER', loading: loading
} // when let action = actions.loadItems() // then expect(action).toEqual(expectedAction)
} })
TESTANDO SEUS COMPONENTES
COMPONENTACTION CREATORS REDUCERS
REDUCERS
TESTANDO SEUS COMPONENTESdescribe('items reducer', () => {
it('should handle START_LOADER', () => { // given const initialState = { loading: false, items: [] } const expectedState = {
loading: true, items: []
} const action = {
type: 'START_LOADER', loading: true
} // when let state = reducer(initialState, action) // then expect(state).toEqual(expectedState)
} })
TESTANDO SEUS COMPONENTES
COMPONENTACTION CREATORS REDUCERS
COMPONENT
TESTANDO SEUS COMPONENTESclass ItemList extends Component {
constructor(props) { super(props)
}
_handleLoader() { if (this.props.loading) {
return <Loader /> }
}
render() { let loader = this._handleLoader() return (
<View> {loader}
</View> )
} }
COMPONENT
TESTANDO SEUS COMPONENTES
describe(‘ItemList Component', () => { it('should render a view without loader', () => {
// given const props = { loading: false, } // when let renderer = TestUtils.createRenderer() renderer.render(<ItemList {...props} />) let output = renderer.getRenderOutput() // then expect(output.type).toBe(View) expect(output.props.children).toEqual(undefined)
} })
TESTANDO SEUS COMPONENTES
COMPONENTACTION CREATORS REDUCERS
TESTANDO SEUS COMPONENTES
COMPONENTACTION CREATORS REDUCERS
SERVER
TESTANDO SEUS COMPONENTES
COMPONENTACTION CREATORS REDUCERS
SERVER
BOAS PRÁTICAS
ESTRUTURA DO SEU PROJETO
FLUXO DE DADOS
TESTANDO SEUS COMPONENTES
ESTRUTURA DO SEU PROJETO
your-project/ android/ ios/ - index.android.js- index.ios.js
ESTRUTURA DO SEU PROJETOyour-project/
android/ ios/ src/ - index.android.js- index.ios.js
ESTRUTURA DO SEU PROJETOyour-project/
android/ ios/ src/
actions/ components/ containers/ reducers/ store/
- index.android.js- index.ios.js
ESTRUTURA DO SEU PROJETO
src/ actions/
- Posts.js - Posts.spec.js
components/ containers/ reducers/ store/
ESTRUTURA DO SEU PROJETOsrc/
actions/ components/
- Loader.js - Loader.spec.js - Post.js - Post.spec.js
containers/ reducers/ store/
ESTRUTURA DO SEU PROJETOsrc/
actions/ components/
base/ - Loader.js - Loader.spec.js
post/ - Post.js - Post.spec.js - PostActions.js - PostActions.spec.js account/ - AccountSettings.js - AccountSettings.spec.js
containers/ reducers/ store/
ESTRUTURA DO SEU PROJETOsrc/
actions/ components/ containers/ - Root.js - Root.spec.js - Timeline.js - Timeline.spec.js - Account.js - Account.spec.js reducers/ store/
ESTRUTURA DO SEU PROJETOsrc/
actions/ components/ containers/ reducers/ - Root.js - Root.spec.js - Posts.js - Posts.spec.js - Account.js - Account.spec.js store/
ESTRUTURA DO SEU PROJETO
src/ actions/ components/ containers/ reducers/ store/ - ConfigureStore.js
SAMPLE APP
SAMPLE APP
https://github.com/Vizir/ReactNativeWorkshop