React Native менен биринчи мобилдик тиркеме жасоо: Бишкек тажрыйбасы (2015)
Кыскача: React Native 0.8–0.12 (2015-жылдагы версия) — JavaScript менен нативдик компоненттерди колдонуу мүмкүнчүлүгүн берди. Android поддержкасы сентябрь 2015-жылы кошулду. Өтө жаш фреймворк болгонуна карабай, production тиркеме үчүн жетиштүү болду.
2015-жылдагы React Native абалы
React Native Facebook тарабынан:
- Март 2015 — iOS үчүн ачык булак катарында жарыяланды (v0.1)
- Сентябрь 2015 — Android поддержкасы кошулду (v0.11)
- Декабрь 2015 — v0.16, туруктуу болуп баштады
Биздин тиркемени июль–август 2015-жылы жаздык: iOS гана, Android жок болчу.
Орнотуу (iOS, 2015-жылдын жазы)
# Node.js 4.x, npm 2.x
npm install -g react-native-cli
react-native init DeliveryApp
cd DeliveryApp
# react-native v0.10 — iOS гана
react-native run-ios
Биринчи компонент: заказдар тизмеси
// OrdersList.js — React Native 0.10 стили
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
ListView,
TouchableHighlight,
ActivityIndicatorIOS,
} = React;
// 2015: class компоненттери жаңы гана киргизилди
// Көпчүлүгү hala createClass колдонду
var OrdersList = React.createClass({
getInitialState: function() {
var ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
return {
dataSource: ds.cloneWithRows([]),
loading: true,
};
},
componentDidMount: function() {
this.fetchOrders();
},
fetchOrders: function() {
fetch('https://api.delivery.kg/v1/orders', {
headers: {
'Authorization': 'Bearer ' + this.props.token,
'Accept': 'application/json',
}
})
.then(response => response.json())
.then(data => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(data.orders),
loading: false,
});
})
.catch(error => {
console.log('Error:', error);
this.setState({ loading: false });
});
},
renderRow: function(order) {
return (
<TouchableHighlight
onPress={() => this.props.onSelectOrder(order)}
underlayColor="#f0f0f0"
>
<View style={styles.row}>
<Text style={styles.orderNum}>#{order.id}</Text>
<Text style={styles.status}>{order.status_label}</Text>
<Text style={styles.address}>{order.address}</Text>
</View>
</TouchableHighlight>
);
},
render: function() {
if (this.state.loading) {
return (
<View style={styles.center}>
<ActivityIndicatorIOS size="large" />
</View>
);
}
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
/>
);
}
});
var styles = StyleSheet.create({
row: {
padding: 15,
borderBottomWidth: 1,
borderBottomColor: '#eee',
},
orderNum: {
fontSize: 16,
fontWeight: 'bold',
color: '#333',
},
status: {
fontSize: 14,
color: '#666',
marginTop: 4,
},
address: {
fontSize: 13,
color: '#999',
marginTop: 2,
},
center: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
module.exports = OrdersList;
Навигация (Navigator — 2015 стандарты)
// App.js — navigator менен экрандар ортосунда которулуу
// React Navigation 2015-жылы жок болчу
// NavigatorIOS же Navigator колдонулду
var React = require('react-native');
var { Navigator, Text, View } = React;
var OrdersList = require('./OrdersList');
var OrderDetail = require('./OrderDetail');
var LoginScreen = require('./LoginScreen');
var App = React.createClass({
render: function() {
return (
<Navigator
initialRoute={{ name: 'Login', index: 0 }}
renderScene={this.renderScene}
navigationBar={
<Navigator.NavigationBar
routeMapper={{
Title: (route) => <Text>{route.name}</Text>,
LeftButton: (route, nav) => route.index > 0 ?
<Text onPress={() => nav.pop()}>Артка</Text> : null,
RightButton: () => null,
}}
/>
}
/>
);
},
renderScene: function(route, navigator) {
switch (route.name) {
case 'Login':
return <LoginScreen onLogin={(token) => {
navigator.push({ name: 'Orders', index: 1, token });
}} />;
case 'Orders':
return <OrdersList
token={route.token}
onSelectOrder={(order) => {
navigator.push({ name: 'OrderDetail', index: 2, order, token: route.token });
}}
/>;
case 'OrderDetail':
return <OrderDetail order={route.order} token={route.token} />;
default:
return <View><Text>Бет табылган жок</Text></View>;
}
}
});
AppRegistry.registerComponent('DeliveryApp', () => App);
API менен иштөө
// api.js — HTTP чалуулар
var API_URL = 'https://api.delivery.kg/v1';
var Api = {
token: null,
setToken: function(token) {
this.token = token;
},
request: function(method, path, body) {
var options = {
method: method,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer ' + this.token,
},
};
if (body) {
options.body = JSON.stringify(body);
}
return fetch(API_URL + path, options)
.then(response => {
if (!response.ok) {
throw new Error('HTTP ' + response.status);
}
return response.json();
});
},
getOrders: function() {
return this.request('GET', '/orders');
},
updateOrderStatus: function(orderId, status) {
return this.request('PUT', '/orders/' + orderId, { status });
},
};
module.exports = Api;
2015-жылдагы реалдуу кыйынчылыктар
1. JavaScript thread жана UI thread бөлүнгөн:
// setTimeout нативдик gesture менен конфликт берди
// Чечим: InteractionManager
var { InteractionManager } = require('react-native');
componentDidMount() {
// Экран ачылып болгондон кийин гана маалымат жүктө
InteractionManager.runAfterInteractions(() => {
this.fetchOrders();
});
}
2. Android 2015-жылы жок болчу:
Биздин клиент Android пайдалануучуларды да кааладı. Android поддержкасы сентябрь 2015-жылы гана чыкты. Ошол убакытка чейин Android версиясын WebView менен жасадык (PhoneGap).
3. Hot Reload жарымынан иштеди:
// 2015: Hot Reload кээде state'ти жоготту
// Чечим: componentDidMount'та маалымат кайра жүктөлсүн
componentDidMount() {
if (__DEV__) {
// Development режиминде ар дайым маалымат жүктө
this.fetchOrders();
}
}
Жыйынтыктар
Тиркемени 8 апта ичинде жасадык. iOS гана, 320 курьер колдонуучу. Нативдик Swift/ObjC тиркемеге салыштырмалуу:
| React Native 2015 | Нативдик iOS | |
|---|---|---|
| Иштеп чыгуу убактысы | 8 апта | 12–15 апта |
| Баага | $2,400 | $4,500 |
| Аткаруу | Жакшы (жүктөлүүдө -20ms) | Мыкты |
| Ката саны (биринчи ай) | 4 краш | 1 краш |
React Native 2015 — толук эмес, бирок иштеген. Андан бери (2025-жылга чейин) фреймворк олуттуу жакшырды.