- Notifications
You must be signed in to change notification settings - Fork55
I use this book to train my team, help them to know how to build React-native app in the right way.
unbug/react-native-train
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
The videos are here -Udemy
Pleaseleave a message or twitter@unbug for further inquiries. Any help will be appreciated :)
- 1 First look
- 2 Components
- 3 Styles
- 4 Architecture
- 5 Data
- 6 Router
- 7 Native Modules (draft)
- 7.1 iOS
- 7.1.1 JS call OC
- 7.1.2 OC call JS
- 7.1.3 Native View Component
- 7.2 Android
- 7.2.1 JS call Java
- 7.2.2 Java call JS
- 7.2.3 Native View Component
- 7.3 Resources
- 7.1 iOS
- 8 Integration (draft)
- 8.1 iOS
- 8.1.1 Package
- 8.1.2 Image
- 8.2 Android
- 8.2.1 Package
- 8.2.2 Image
- 8.3 Before publishing
- 8.4 Resources
- 8.1 iOS
- 9 Hot Update (draft)
- 9.1 iOS
- 9.2 Android
- 9.3 Resources
- 10 Performance (draft)
- Resources
What we really want is the user experience of the native mobile platforms, combined with the developer experience we have when building with React on the web.
With a bit of work, we can make it so the exact same React that's on GitHub can power truly native mobile applications. The only difference in the mobile environment is that instead of running React in the browser and rendering to divs and spans, we run it an embedded instance of JavaScriptCore inside our apps and render to higher-level platform-specific components.
It's worth noting that we're not chasing“write once, run anywhere.” Different platforms have different looks, feels, and capabilities, and as such, we should still be developing discrete apps for each platform, but the same set of engineers should be able to build applications for whatever platform they choose, without needing to learn a fundamentally different set of technologies for each. We call this approach“learn once, write anywhere.”
Requirement followGetting Started
Generate a new React Native project
react-native init testRn
Build & run project
react-native run-ios
or open
testRn/ios/testRn.xcodeproj
and build with XCode's play button
or if the app already builded, start the webserver
npm start//orreact-native start

2.Chrome Devtools
console.log('some text');console.dir({a:1, b:2, c:3});debugger;//breaking point
5.inspect
Open AtomCommand Palette package withcmd-shift-p
and search "inspector", then click "Nuclide React Native Inspector:Show"
6.1 Deploy to real deviceproject_name/ios/project_name/AppDelegate.m
//jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];/** * OPTION 2 * Load from pre-bundled file on disk. The static bundle is automatically * generated by the "Bundle React Native code and images" build step when * running the project on an actual device or running the project on the * simulator in the "Release" build configuration. */jsCodeLocation= [[NSBundlemainBundle]URLForResource:@"main" withExtension:@"jsbundle"];
6.2 Debug in real device
1.project_name/ios/project_name/AppDelegate.m
jsCodeLocation= [NSURLURLWithString:@"http://172.28.0.230:8081/index.ios.bundle?platform=ios&dev=true"];
2.node_modules/react-native/Libraries/WebSocket/RCTWebSocketExecutor.m
if (!_url) {NSUserDefaults*standardDefaults= [NSUserDefaultsstandardUserDefaults];NSIntegerport= [standardDefaultsintegerForKey:@"websocket-executor-port"] ?:8081;NSString*URLString= [NSStringstringWithFormat:@"http://172.28.0.230:%zd/debugger-proxy?role=client",port];_url= [RCTConvertNSURL:URLString]; }
- React Native: Bringing modern web techniques to mobile
- React Native通信机制详解
- React Native 调研报告
- React Native概述:背景、规划和风险
- JavaScriptCore
- React Native iOS 真机调试
1.MyComponent.js
//define componentclassMyComponentextendsReact.Component{render(){return<Text>My component!</Text>;}}//export componentexportdefaultMyComponent;
2.Main.js
//import componentimportMyComponentfrom'./MyComponent';classMainextendsReact.Component{render(){//use componentreturn<MyComponent>;}}
3.AppRegistry
AppRegistry.registerComponent('MyApp',()=>Main);
.....render(){consttxt='Hello';functionsay(name){return'I am '+name;}return(<View><Text>This is a title!</Text><Text>{txt}</Text><View><Text>{say('React')}</Text></View></View>);}.....
.....import{StyleSheet,Text,View,Image}from'react-native';classMainextendsComponent{render(){return(<View><Imagesource={require('./img/bg.png')}><Imagesource={require('./img/icon.png')}/><Text> some text!</Text></Image></View>);}}
Instantiation
1.1 The lifecycle methods that are called the first time an instance is created
- getDefaultProps
- getInitialState
- componentWillMount
- render
- componentDidMount
1.2 For all subsequent uses of that component class:
- getInitialState
- componentWillMount
- render
- componentDidMount”
Lifetime
- componentWillReceiveProps
- shouldComponentUpdate // return true|false
shouldComponentUpdate(nextProps,nextState){returnnextProps.id!==this.props.id;}
- componentWillUpdate //not called for the initial render
- render
- componentDidUpdate
Teardown & cleanup
- componentWillUnmount
1.props: properties are passed to a component and can hold any data
classUserextendsComponent{render(){constuser=this.props.data;this.props.onReady('I am ready!');return(<View><Text> score:{this.props.score} type:{this.props.type} Name:{user.name} Age:{user.age}</Text></View>);}}//dufaultPropsUser.propTypes={score:React.PropTypes.number};User.defaultProps={score:0};varuser={name:'foo',age:21};classMainextendsComponent{handleReady(str){console.log(str);}render(){return(<View><Usertype="Dev"data={user}onReady={this.handleReady}/></View>);}}
2.state: State differs from props in that it is internal to the component.
classTimerextendsComponent{constructor(props){super(props);this.state={count:0};}componentDidMount(){letthat=this;setInterval(function(){that.increase();},1000);}increase(){this.setState({count:this.state.count+1});}render(){return(<View><Text>count:{this.state.count}</Text></View>);}}classMainextendsComponent{render(){return(<View><Timer/></View>);}}
3.props VSstate
- Use props to pass data and settings through the component tree.
- Never modify this.props inside of a component; consider props immutable.
- Use props to for event handlers to communicate with child components.
- Use state for storing simple view state like wether or not drop-down options are visible.
- Never modify this.state directly, use this.setstate instead.
4.Stateless Component
constHeading=({title})=><Text>{title}</Text>;.....<Headingtitle="test title"/>.....
1.Basic events
classTouchextendsComponent{handlePress(){console.log('press');}handleLongPress(){console.log('longPress');}render(){return(<TouchableHighlightonPress={this.handlePress}onLongPress={this.handleLongPress}><View><Text>Press me!</Text></View></TouchableHighlight>);}}
1.2.<TextInput/>
classTestextendsComponent{//...//handle events//...render(){return(<TextInputonBlur={...}onChange={...}onEndEditing={...}onSelectionChange={...}onSubmitEditing={...}</TextInput>);}}
//keyboardWillShow, keyboardDidShow, keyboardWillHide, keyboardDidHide//keyboardWillChangeFrame, keyboardDidChangeFrame//add the listenervarlistener=DeviceEventEmitter.addListener('keyboardWillShow',(e)=>{console.log('Event is fired!');});//remove the listenerlistener.remove();
classTestextendsComponent{/* Capture handles *///the responder system bubbles up from the deepest component,//a parent View wants to prevent the child from becoming responder on a touch starthandleStartShouldSetResponderCapture(evt){returntrue;}//the responder system bubbles up from the deepest component,//a parent View wants to prevent the child from becoming responder on a touch movehandleMoveShouldSetResponderCapture(evt){returntrue;}/* Lifecycle handles *///Does this view want to become responder on the start of a touch?handleStartShouldSetResponder(evt){returntrue;}//Called for every touch move on the View when it is not the responder://does this view want to "claim" touch responsiveness?handleMoveShouldSetResponder(evt){returntrue;}//The View is now responding for touch events.handleResponderGrant(evt){console.log('you are touching me');}//Something else is the responder right now and will not release ithandleResponderReject(evt){console.log('please wait in line');}/* event handles *///touch movehandleResponderMove(evt){console.log('touch move at:','X='+evt.pageX,'Y='+evt.pageY);}//touch end/uphandleResponderRelease(evt){console.log('touch end');}//Something else wants to become responder. Should this view release the responder?handleResponderTerminationRequest(evt){returntrue;}//touch cancelhandleResponderTerminate(evt){console.log('touch canceled');}render(){return(<ViewonStartShouldSetResponderCapture={this.handleStartShouldSetResponderCapture}onMoveShouldSetResponderCapture={this.handleMoveShouldSetResponderCapture}onStartShouldSetResponder={this.handleStartShouldSetResponder}onMoveShouldSetResponder={this.handleMoveShouldSetResponder}onResponderGrant={this.handleResponderGrant}onResponderReject={this.handleResponderReject}onResponderMove={this.handleResponderMove}onResponderRelease={this.handleResponderRelease}onResponderTerminationRequest={this.handleResponderTerminationRequest}onResponderTerminate={this.handleResponderTerminate}><Text>Press me!</Text></View>);}}
2.3 evt is a synthetic touch event with the following form nativeEvent:
- changedTouches - Array of all touch events that have changed since the last event
- identifier - The ID of the touch
- locationX - The X position of the touch, relative to the element
- locationY - The Y position of the touch, relative to the element
- pageX - The X position of the touch, relative to the root element
- pageY - The Y position of the touch, relative to the root element
- target - The node id of the element receiving the touch event
- timestamp - A time identifier for the touch, useful for velocity calculation
- touches - Array of all current touches on the screen
3.1
this._panResponder=PanResponder.create({// Ask to be the responder:onStartShouldSetPanResponder:(evt,gestureState)=>true,onStartShouldSetPanResponderCapture:(evt,gestureState)=>true,onMoveShouldSetPanResponder:(evt,gestureState)=>true,onMoveShouldSetPanResponderCapture:(evt,gestureState)=>true,//touch startonPanResponderGrant:(evt,gestureState)=>{},//touch moveonPanResponderMove:(evt,gestureState)=>{},onPanResponderTerminationRequest:(evt,gestureState)=>true,//touch end/uponPanResponderRelease:(evt,gestureState)=>{},//touch cancelonPanResponderTerminate:(evt,gestureState)=>{},onShouldBlockNativeResponder:(evt,gestureState)=>true,});
3.2 A gestureState object has the following:
- stateID - ID of the gestureState- persisted as long as there at least one touch on screen
- moveX - the latest screen coordinates of the recently-moved touch
- moveY - the latest screen coordinates of the recently-moved touch
- x0 - the screen coordinates of the responder grant
- y0 - the screen coordinates of the responder grant
- dx - accumulated distance of the gesture since the touch started
- dy - accumulated distance of the gesture since the touch started
- vx - current velocity of the gesture
- vy - current velocity of the gesture
- numberActiveTouches - Number of touches currently on screen
3.3PanResponder example in UIExplorer
1.Declare Style
conststyles=StyleSheet.create({container:{flex:1,backgroundColor:'blue',},text:{fontSize:14,color:'red'}});
2.Using Styles
classMainextendsComponent{render(){return(<Viewstyle={styles.container}><Textstyle={styles.text}>I am red.</Text></View>);}}
3.Properties
The main idea behind the flex layout is to give the container the ability to alter its items' width/height (and order) to best fill the available space (mostly to accommodate to all kind of display devices and screen sizes). A flex container expands items to fill available free space, or shrinks them to prevent overflow.
2.flex:1
conststyles=StyleSheet.create({container:{flex:1},header:{height:200,backgroundColor:'red'},main:{flex:1,backgroundColor:'blue'},footer:{height:200,backgroundColor:'green'},text:{color:'#ffffff',fontSize:80}});
3.flexDirection:'row'|'column'
4.justifyContent:'flex-start'|'flex-end'|'center'|'space-between'|'space-around'
5.alignItems:'flex-start'|'flex-end'|'center'|'stretch'
6.alignSelf:'auto'|'flex-start'|'flex-end'|'center'|'stretch'
7.flexWrap:'wrap'|'nowrap'
8.Box model
width = borderLeftWidth(25)+paddingLeft(25)+100+borderRightWidth(25)+paddingRight(25)=200
height = borderTopWidth(25)+paddingTop(25)+100+borderBottomWidth(25)+paddingBottom(25)=200
classMainextendsComponent{render(){return(<Viewstyle={styles.container}><Viewstyle={styles.header}><Textstyle={styles.text}>200X100</Text></View><Viewstyle={styles.main}><Viewstyle={styles.mainContent}><Textstyle={styles.text}>100X100</Text></View></View><Viewstyle={styles.footer}><Textstyle={styles.text}>200X100</Text></View></View>);}}conststyles=StyleSheet.create({container:{flex:1,justifyContent:'center',alignItems:'center'},header:{height:100,width:200,backgroundColor:'red'},main:{height:200,width:200,padding:25,borderWidth:25,borderColor:'black',margin:25,backgroundColor:'blue'},mainContent:{flex:1,justifyContent:'center',alignItems:'center',backgroundColor:'red'},footer:{height:100,width:200,backgroundColor:'green'},text:{color:'#ffffff',fontSize:20}});
1.absolute
classPositionextendsComponent{render(){return(<Viewstyle={styles.container}><Viewstyle={styles.box1}><Textstyle={styles.text}>1</Text></View><Viewstyle={styles.box2}><Textstyle={styles.text}>2</Text></View><Viewstyle={styles.box3}><Textstyle={styles.text}>3</Text></View></View>);}}conststyles=StyleSheet.create({container:{flex:1},box1:{position:'absolute',top:40,left:40,width:100,height:100,backgroundColor:'red'},box2:{position:'absolute',top:80,left:80,width:100,height:100,backgroundColor:'blue'},box3:{position:'absolute',top:120,left:120,width:100,height:100,backgroundColor:'green'},text:{color:'#ffffff',fontSize:80}});
box2:{position:'absolute',top:80,left:80,width:100,height:100,backgroundColor:'blue',transform:[{'translate':[0,0,1]}]},
3.relative(default)
classRelativeextendsComponent{render(){return(<Viewstyle={styles.container}><Viewstyle={styles.box1}><Textstyle={styles.text}>1</Text><Viewstyle={styles.ball}/></View><Viewstyle={styles.box2}><Textstyle={styles.text}>2</Text></View></View>);}}conststyles=StyleSheet.create({container:{flex:1},box1:{position:'relative',top:40,left:40,width:100,height:100,backgroundColor:'red'},box2:{position:'absolute',top:100,left:100,width:100,height:100,backgroundColor:'blue'},ball:{position:'absolute',top:40,left:40,width:40,height:40,borderRadius:20,backgroundColor:'yellow'},text:{color:'#ffffff',fontSize:80}});
4.fixed
classFixedextendsComponent{render(){return(<Viewstyle={styles.container}><Viewstyle={styles.tbar}><Textstyle={styles.text}>Fixed top bar</Text></View><ScrollViewstyle={styles.main}><Viewstyle={styles.item}><Textstyle={styles.text}>1</Text></View><Viewstyle={styles.item}><Textstyle={styles.text}>2</Text></View><Viewstyle={styles.item}><Textstyle={styles.text}>3</Text></View></ScrollView><Viewstyle={styles.bbar}><Textstyle={styles.text}>Fixed bottom bar</Text></View></View>);}}conststyles=StyleSheet.create({container:{flex:1},tbar:{width:375,height:100,borderBottomWidth:5,borderColor:'black',backgroundColor:'red'},main:{flex:1},item:{height:200,width:375,marginTop:10,backgroundColor:'green'},bbar:{width:375,height:100,borderTopWidth:5,borderColor:'black',backgroundColor:'red'},text:{color:'#ffffff',fontSize:40}});
1.window size
letwinSize=Dimensions.get('window');console.log(winSize);classSizeextendsComponent{render(){return(<Viewstyle={styles.container}><Viewstyle={styles.block}/><Textstyle={styles.text}>some text</Text></View>);}}conststyles=StyleSheet.create({container:{flex:1,alignItems:'flex-start'},block:{height:100,width:winSize.width,backgroundColor:'red'},text:{color:'#ffffff',fontSize:40/winSize.scale,backgroundColor:'blue'}});
2.onLayout
classSizeextendsComponent{handleTextLayout(evt){console.log(evt.nativeEvent.layout);}render(){return(<Viewstyle={styles.container}><Viewstyle={styles.block}/><Textstyle={styles.text}onLayout={this.handleTextLayout}>some text</Text></View>);}}
1.pass styles as props
classInheritanceStyleextendsComponent{render(){return(<Viewstyle={this.props.parentColor}></View>);}}classMainextendsComponent{handleReady(str){console.log(str);}render(){return(<Viewstyle={styles.container}><InheritanceStyleparentColor={styles.blue}/></View>);}}conststyles=StyleSheet.create({container:{flex:1},blue:{flex:1,backgroundColor:'blue'}});
2.concatenation styles
BaseStyles.js
import{StyleSheet,Dimensions}from'react-native';letwinSize=Dimensions.get('window');constBaseStyles=StyleSheet.create({text:{fontSize:40/winSize.scale}});exportdefaultBaseStyles;
importBaseStylesfrom'./BaseStyles';classInheritanceStyleextendsComponent{render(){return(<Viewstyle={this.props.parentColor}><Textstyle={[BaseStyles.text,styles.text]}> this is a long text</Text></View>);}}conststyles=StyleSheet.create({text:{color:'#ffffff'}});
- A Complete Guide to Flexbox
- A Visual Guide to CSS3 Flexbox Properties
- Understanding Flex Direction
- DEMO scripts for this chapter
1.MVC problems
2.Flux
1.Actions & Action Creators
//action typeconstADD_TODO='ADD_TODO';//action creator, semantic methods that create actions//collected together in a module to become an APIfunctionaddTodoAction(title,hour){//action, an object with a type property and new data, like eventsreturn{type:ADD_TODO, title, hour}}
2.Reducers
//a function that accepts an accumulation and a value and returns a new accumulation.functiontodoReducers(state=[],action){switch(action.type){caseADD_TODO://always return a new state, never mutate old statereturn[{id:Utils.GUID(),title:action.title,endTime:getEndTime(action.hour),completed:false}, ...state]default://return default statereturnstate}}
3.Store
import{createStore}from'redux';//1. define storeletstore=createStore(todoReducers);classAppextendsComponent{constructor(props){super(props);this.state={todos:[]};}componentDidMount(){//2. subscribe storethis.unsubscribeStore=store.subscribe(()=>{//3. getStatethis.setState({todos:store.getState()});});}componentWillUnmount(){//5. unsubscribe storethis.unsubscribeStore();}renderTodoList=()=>{//reder todo listreturnthis.state.todos.map((todo)=>{return<Textkey={todo.id}>Todo:{todo.title}</Text>});}handleAddTodo=()=>{//4. dispatching actionsstore.dispatch(addTodoAction('Create a new todo',8));}render(){return(<View><TouchableHighlightonPress={this.handleAddTodo}><Text>Add Todo</Text></TouchableHighlight><ScrollView>{this.renderTodoList()}</ScrollView></View>);}}
4.Data flow
4.2react-redux
1.Actions
import*asnavigationActionsfrom'./navigation';import*astodosActionsfrom'./todos';exportdefault{...navigationActions, ...todosActions};
2.combineReducers()
import{combineReducers}from'redux';importnavigationfrom'./navigation';importtodosfrom'./todos';constrootReducer=combineReducers({ navigation, todos});exportdefaultrootReducer;
3.Application state by configureStore()
import{createStore}from'redux';importreducersfrom'../reducers';exportdefaultfunctionconfigureStore(){conststore=createStore(reducers);returnstore;}
4.mapStateToProps & mapDispatchToProps & bindActionCreators
import{bindActionCreators}from'redux';import{connect}from'react-redux';classAppextendsComponent{renderTodoList=()=>{//reder todo listreturnthis.props.todos.map((todo)=>{return<Textkey={todo.id}>Todo:{todo.title}</Text>});}handleAddTodo=()=>{this.props.actions.addTodoAction('Create a new todo',8);}render(){return(<View><TouchableHighlightonPress={this.handleAddTodo}><Text>Add Todo</Text></TouchableHighlight><ScrollView>{this.renderTodoList()}</ScrollView></View>);}}functionmapStateToProps(state){return{todos:state.todos};}functionmapDispatchToProps(dispatch){return{actions:bindActionCreators(Actions,dispatch)}}exportdefaultconnect(mapStateToProps,mapDispatchToProps)(App);
5.Passing the Store with<Provider/>
importReact,{Component}from'react';import{Provider}from'react-redux';importAppfrom'./containers/App';importconfigureStorefrom'./store/configureStore';classRootextendsComponent{render(){return(<Providerstore={configureStore()}><App/></Provider>);}}exportdefaultRoot;
1.Presentational and Container Components
Presentational Components | Container Components | |
---|---|---|
Purpose | How things look (markup, styles) | How things work (data fetching, state updates) |
Aware of Redux | No | Yes |
To read data | Read data from props | Subscribe to Redux state |
To change data | Invoke callbacks from props | Dispatch Redux actions |
Are written | By hand | Usually generated by React Redux |
2.components/home-view
&containers/HomeView
2.1 home-view components
2.2 HomeView container
import{Header,Main,}from'../components/home-view';importActionsfrom'../actions';classHomeViewextendsComponent{render(){return(<View><Header{...this.props}/><Main{...this.props}isVisible={this.state.isVisible}/></View>);}}functionmapStateToProps(state){return{todos:state.todos};}functionmapDispatchToProps(dispatch){return{actions:bindActionCreators(Actions,dispatch)}}exportdefaultconnect(mapStateToProps,mapDispatchToProps)(HomeView);
1.Overview
2.Structure
3.Containers & Components
1.Containers & Components
1.1. Container file:
src/containers/ModuleNameView.js
Component files:
src/components/module-name-view-index.js-Main.js-Header.js- ...-img-icon@2x.png-icon@3x.png
1.2. Event name:
handleEventName=()=>{//todo}...<MyComponentonEventName={this.handleEventName}/>
1.3. Render methods:
renderMethodName=()=>{//todo}render(){return(<View>{this.renderMethodName()}</View>);}
1.4. mapStateToProps & mapDispatchToProps
functionmapStateToProps(state){return{todos:state.todos};}functionmapDispatchToProps(dispatch){return{actions:bindActionCreators(Actions,dispatch)}}
2.actionssrc/actions
index.jstodos.jsnavigation.js
2.1src/constants/ActionTypes.js
exportconstSWITCH_MAIN_TAB='SWITCH_MAIN_TAB';
2.2```src/actions/todos.js````
import*astypesfrom'../constants/ActionTypes'exportfunctionaddTodo(title,hour){return{type:types.ADD_TODO, title, hour}}
3.reducerssrc/reducers
index.jstodos.jsnavigation.js
3.1.src/reducers/todos.js
import{ADD_TODO,DELETE_TODO,EDIT_TODO,COMPLETE_TODO}from'../constants/ActionTypes'constinitialState=[]exportdefaultfunctiontodos(state=initialState,action){switch(action.type){caseADD_TODO://tododefault:returnstate}}
4.stylessrc/styles
index.jsBasic.jsTheme.js
4.1src/styles/Basic.js
import{StyleSheet,Dimensions}from'react-native';letwinSize=Dimensions.get('window');constBasic=StyleSheet.create({text:{fontSize:32/winSize.scale}});exportdefaultBasic;
4.2src/styles/Theme.js
//colorsconstcolor={green:'#00551e',brown:'#693504',red:'#db2828'}//otherconstactive={opacity:0.6}exportdefault{color, active}
4.3import {Theme, BasicStyle} from '../../styles';
- Flux
- Facebook: MVC Does Not Scale, Use Flux Instead
- Redux
- fluxchat by Bill Fisher
- Introduce Flux & react in practices (KKBOX)
- react-flux-fluent-2015 by Bill Fisher
- Flux TodoMVC Example
- Todo React Native App
- Fetch
- XMLHttpRequest API
- WebSocket
1.applyredux-thunk middleware
import{applyMiddleware,createStore,compose}from'redux';importthunkfrom'redux-thunk';importcreateLoggerfrom'redux-logger';importreducersfrom'../reducers';varmiddlewares=compose(applyMiddleware(thunk),autoRehydrate());exportdefaultfunctionconfigureStore(){conststore=createStore(reducers,undefined,middlewares);returnstore;}
2.start & end action types
//todo action typesexportconstSTART_FETCH_ALL_TODOS='START_FETCH_ALL_TODOS';exportconstFETCH_ALL_TODOS='FETCH_ALL_TODOS';
3.fetch flow
import*astypesfrom'../constants/ActionTypes';import*asAPIsfrom'../constants/ServerAPIs';functionshouldFetchAllTodos(state){constdata=state.todos;if(data&&data.isFetchingAllTodos){returnfalse}returntrue;}exportfunctionfetchAllTodos(){returnasync(dispatch,getState)=>{//verifyif(!shouldFetchAllTodos(getState())){returnPromise.resolve();}//dispatch fetch start actiondispatch({type:types.START_FETCH_ALL_TODOS});//fetchingconstresponse=awaitfetch(APIs.allTodos);//responseconstdata=awaitresponse.json();//dispatch fetch end actionreturndispatch({type:types.FETCH_ALL_TODOS, data});}}
4.reducer
exportdefaultfunctiontodos(state=initialState,action){switch(action.type){casetypes.START_FETCH_ALL_TODOS:returnObject.assign({},state,{isFetchingAllTodos:true});casetypes.FETCH_ALL_TODOS:returnObject.assign({},state,{isFetchingAllTodos:false,data:action.data.data.reduce(function(pre,cur){//remove duplicates!pre.find(key=>key.id===cur.id)&&pre.push(cur);returnpre;},[...state.data])}); ... ...default:returnstate}}
5.dispatch & render
...componentDidMount(){//fetch data from serverthis.props.actions.fetchAllTodos();}......renderLoading=()=>{if(this.props.todos.isFetchingAllTodos){return(<Viewstyle={styles.loading}><Textstyle={styles.loadingText}>Loading...</Text></View>)}returnnull;}...
2.applyredux-persist middlewear
import{AsyncStorage}from'react-native';import{applyMiddleware,createStore,compose}from'redux';importthunkfrom'redux-thunk';import{persistStore,autoRehydrate}from'redux-persist';importreducersfrom'../reducers';varmiddlewares=compose(applyMiddleware(thunk),autoRehydrate());exportdefaultfunctionconfigureStore(){conststore=createStore(reducers,undefined,middlewares);persistStore(store,{storage:AsyncStorage});returnstore;}
1.define routes
importMainTabsViewfrom'./MainTabsView';importEditViewfrom'./EditView';importBroswerViewfrom'./BroswerView';constROUTES={ MainTabsView, BroswerView, EditView};
2.config Navigator
classAppextendsComponent{renderScene=(route,navigator)=>{letScene=ROUTES[route.name];return<Scene{...route}navigator={navigator}/>;}configureScene=(route,routeStack)=>{switch(route.name){case'EditView':returnNavigator.SceneConfigs.FloatFromBottom;default:returnNavigator.SceneConfigs.PushFromRight;}}render(){return(<Viewstyle={styles.container}><StatusBarbarStyle="light-content"/><NavigatorinitialRoute={{name:'MainTabsView'}}renderScene={this.renderScene}configureScene={this.configureScene}/></View>)}}
3.forward & back
...handleEdit=()=>{//Navigate forward to a new scenethis.props.navigator.push({name:'EditView',data:this.props.data});}...
...close=()=>{//Transition back and unmount the current scenethis.props.navigator.pop();}...
4.onDidFocus & onWillFocus
...componentDidMount(){this.currentRoute=this.props.navigator.navigationContext.currentRoute;this.bindEvents();}componentWillUnmount(){this.unBindEvents();}bindEvents=()=>{this.willFocusSubscription=this.props.navigator.navigationContext.addListener('willfocus',(event)=>{if(this.currentRoute!==event.data.route){this.setState({isVisible:false});}});this.didFocusSubscription=this.props.navigator.navigationContext.addListener('didfocus',(event)=>{if(this.currentRoute===event.data.route){this.setState({isVisible:true});}});}unBindEvents=()=>{this.willFocusSubscription.remove();this.didFocusSubscription.remove();}...
Somewhere in your RN codes, insert:
console.log(__fbBatchedBridge);
then in your debugger (http://localhost:8081/debugger-ui) you'll see something like below,
that's what we're going to talk about in this chapter.
Most of the time we are not starting a new app, we just want to use react-native to develop some new features, so integration should be a necessary skill for react-native developers.
Assume that you have create some features in the AwesomeProject, you want to create an exactly same environment for your current project.
Notice that version is very important after your app published. If you want to upgrade react-native and package codes, that almost means you don't want to maintain the old version any longer.
Requirement : [CocoaPods](https://cocoapods.org/) After pod **version > 1.0**, you need to identify the target. Create 'Podfile' in project root folder :```
target 'MyiOSApp' dopod 'React', :path => '../../AwesomeProject/node_modules/react-native', :subspecs => ['Core','RCTImage','RCTNetwork','RCTText','RCTWebSocket',]end```
thenpod install
react-native bundle --platform ios --dev false --entry-file index.ios.js --bundle-output ios/bundle/index.ios.bundle --assets-dest ios/bundle``` # 8.2 AndroidAt first I followed the official instruction (which seems very simple) but lots of build or runtimeerror occurs 😂.Such as:``` Can't find variable: __fbBatchedBridge (<unknown file>:1)``````java.lang.UnsatisfiedLinkError: could find DSO to load: libreactnativejni.so``````android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@5d992cf -- permission denied for this window type```But the demo works corrcetly, so I decided to copy the build settings of it. And finally it works normally on my Nexus 5X. Steps:- Add the path to the root gradle file,- Modify the app gradle file, *1. Official demo use this variable to control wheather building different apps for cpus, that will reduce the size of each app, I just ignore it here. *2. The version support package matters..As react-native Android use many open source projects, if you use some of them already, you should check the version or exclude the from dependencies. [The list currently](https://github.com/facebook/react-native/blob/master/ReactAndroid/build.gradle) ``` compile 'com.android.support:appcompat-v7:23.0.1' compile 'com.android.support:recyclerview-v7:23.0.1' compile 'com.facebook.fresco:fresco:0.11.0' compile 'com.facebook.fresco:imagepipeline-okhttp3:0.11.0' compile 'com.fasterxml.jackson.core:jackson-core:2.2.3' compile 'com.google.code.findbugs:jsr305:3.0.0' compile 'com.squareup.okhttp3:okhttp:3.2.0' compile 'com.squareup.okhttp3:okhttp-urlconnection:3.2.0' compile 'com.squareup.okhttp3:okhttp-ws:3.2.0' compile 'com.squareup.okio:okio:1.8.0' compile 'org.webkit:android-jsc:r174650'
At first I followed the official instruction (which seems very simple) but lots of build or runtimeerror occurs 😂.
Such as:
Can't find variable: __fbBatchedBridge (<unknown file>:1)
java.lang.UnsatisfiedLinkError: could find DSO to load: libreactnativejni.so
android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@5d992cf -- permission denied for this window type
But the demo works corrcetly, so I decided to copy the build settings of it. And finally it works normally on my Nexus 5X. Steps:
*1. Official demo use this variable to control wheather building different apps for cpus, that will reduce the size of each app, I just ignore it here.
*2. The version support package matters..As react-native Android use many open source projects, if you use some of them already, you should check the version or exclude the from dependencies.The list currently
compile 'com.android.support:appcompat-v7:23.0.1' compile 'com.android.support:recyclerview-v7:23.0.1' compile 'com.facebook.fresco:fresco:0.11.0' compile 'com.facebook.fresco:imagepipeline-okhttp3:0.11.0' compile 'com.fasterxml.jackson.core:jackson-core:2.2.3' compile 'com.google.code.findbugs:jsr305:3.0.0' compile 'com.squareup.okhttp3:okhttp:3.2.0' compile 'com.squareup.okhttp3:okhttp-urlconnection:3.2.0' compile 'com.squareup.okhttp3:okhttp-ws:3.2.0' compile 'com.squareup.okio:okio:1.8.0' compile 'org.webkit:android-jsc:r174650'
react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output android/bundle/index.android.bundle --assets-dest android/bundle/
- Turn off debug Settings.
- Implementation of exception handler.
This chapter can be applied to all react apps.
React is usually fast, but you still can improve performance by optimizing functionshouldComponentUpdate. By default it returns true, if returns false, the render function will be skipped.
This function is frequently invoked when states or props are changed. So it's important tokeep it simple and fast.When you calledsetState
, therender
function will always be excuted even if previous states are equal to current. This is where we can make some optimization.
In demo1, when click button, it will set same state, but render times will still increase.
In demo2, we check the value of name is equal to before or not, if equal return false, then we reduce the times of render function.
But if our states structure is complicated, such as{ a: { b: { c: [1, 2, 3] }}}
, we have to compare them deeply. This is obviously against the rules we mentioned above, ** keep shouldComponentUpdate simple**
Immutable is a concept fromfunctional programming, one of immutable data features is that it can not be modified after being created. So there are some algorithm to create hash for every data structure(for moredetail).We can use this feature to prevent deeply compare, shallow compare is enough.Here we will useimmutable-js from facebook
In demo3, we click first button several times, times will only plus one time, click second button , times will increase.
- React.js Reconciliation
- Reconciliation
- Advanced Performance
- Immutable-js
- ShouldComponentUpdate
- Functional Programming
Created by@unbug:
- MIHTool - iOS Web Debugger Pro: MIHTool helps Front-End Engineers to debug and optimize their webpages on iPad and iPhone.
- Codelf - 变量命名神器: Organize your GitHub stars and repositories. Search over projects from GitHub to find real-world usage variable names.
- js-middleware: Powerful Javascript Middleware Pattern implementation, apply middleweares to any object. A painless solution to make codes as scalable and maintainable as ReduxJS and ExpressJS.
- SAY NO TO SUICIDE PUBLIC LICENSE: We've lost so many genius developers, who committed suicide, such as Aaron Hillel Swartz (November 8, 1986 – January 11, 2013). As a developer, the community needs you, the world needs you, please keep yourself alive.
About
I use this book to train my team, help them to know how to build React-native app in the right way.
Topics
Resources
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Contributors4
Uh oh!
There was an error while loading.Please reload this page.