Nie znam React, to chyba React Native nie dla mnie… Tak myślałem jakiś czas temu, ale dziś zapoznałem się w pewnej mierze z tym frameworkiem i muszę przyznać, że może się podobać. Upraszcza w znaczny sposób pewne standardowe rzeczy. W dodatku zachęciło mnie to do samego Reacta, który - jeśli odpowiada w pewnym stopniu Native Reactowi - nie powinien wydawać się taki zły, a może być naprawdę wygodny i przyjemny. Jedynie samodzielne opakowywanie natywnego API w Objective-C nie wydaje się już do końca takie wygodne (brakuje mi czegoś na kształt automatycznej projekcji języka jak w WinRT).
No HTML, 100% native
debug w Chrome developer tools
stylowanie w składni bardzo podobnej do CSS
layout za pomocą FlexBox
narzędzia: Xcode, Homebrew
brew install node
brew install –HEAD watchman
brew install flow
npm install –g react-native-cli
react-native init GithubBrowser
otwieramy wygenerowany projekt Xcode
index.ios.js
‘use strict’;
var React = require(‘react-native’);
var {
AppRegistry,
StyleSheet,
Text,
View,
} = React
var GithubBrowser = React.createClass({
render: function() {
var msg = ‘abc’;
return (
<View style={styles.container}>
<Text style={styles.welcome}>
{msg}
</Text>
</View>
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
…
},
welcome: {
fontSize: 20,
textAlign: ‘center’,
margin: 10,
},
…
});
AppRegistry.registerComponent(‘GithubBrowser’, () => GithubBrowser);
github Samples
npm –i
plik komponentu Login:
…
var Text = React.Text;
… <Text style={{color: ‘white’}}>
module.exports = Login;
inny view:
<Login>
<Image … source={require(‘image!Octocat’)} //obrazki w Images.xcassets w Xcode
“styl”: width, height - tylko liczby (nie piksele)
<TextInput … />
przycisk:
<TouchableHighlight … >
<Text …>
Log in
</Text>
</TouchableHighlight>
ES6
class Login extends Component {
constructor(props) {
super(props);
this.state = {
showProgress: false
}
}
render() {
return …
}
}
<TextInput onChangeText={(text) =>this.setState({username: text)}
<TouchableHighlight onPress={this.onLoginPressed.bind(this)}
onLoginPressed() {
…
}
Progress
<ActivityIndicatorIOS animating={this.state.showProgress} size=”large” />
HTTP
fetch(‘http://…’)
.then((response)=> {
return response.json();
})
.then((results) => {
…
});
Autentykacja
var buffer = require(‘buffer’);
…
var b = new buffer.Buffer(‘hello’);
//b.toString(‘base64’);
---
var buffer = require(‘buffer’);
class AuthService {
login(creds, cb) {
…
}
}
module.exports = new AuthService();
---
var authService = require(‘./AuthService’);
authService.login({
username: this.state.username,
password: this.state.password
}, (results)=> {
this.setState(Object.assign({
showProgress: false
}, results));
});
przejście do innego widoku:
render:
if(this.state.isLoggedIn)
return …
else
return …
//początkowy stan komponentu/widoku
getInitialState: function() {
return {
isLoggedIn: false
};
}
dane:
AsyncStorage.setItem()
AsyncStorage.multiSet()
var AsyncStorage = require(‘react-native’).AsyncStorage;
AsyncStorage.multiSet([
[‘auth’, encodedAuth],
[‘user’, JSON.stringify(results]]
], …)
odczyt
npm install lodash –save
var _ = require(‘loadash’);
getAuthInfo(cb) {
AsyncStorage.multiGet([authKey, userKey], (err, val) => {
if (err) {
return cb(err);
}
if(!val) {
return cb();
}
var zippedObj = _.zipObject(val);
if (!zippedObj[authKey]) { return cb(); }
var authInfo = {
header: {
Authorization: ‘Basic ‘ + zippedObj[authKey]
},
user: JSON.parse(zippedObj[userKey])
}
return cb(null, authInfo);
});
}
…
componentDidMount: function() {
AuthService.getAuthInfo((err, authInfo)=> {
this.setState({
…
isLoggedIn: authInfo != null
})
})
},
Zakładki:
this.state = {
selectedTab: ‘feed’
}
<TabBarIOS …>
<TabBarIOS.Item
title=”Feed”
selected={this.state.selectedTab == ‘feed’}
icon={require(‘image!inbox’)}
onPress={()=>this.setState({selectedTab: ‘feed’})}
>
<Text …>Tab 1</Text>
</TabBarIOS.Item>
</TabBarIOS>
listview
constructor:
var ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 != r2
});
this.state = {
dataSource: ds.cloneWithRows([‘A’, ‘B’, ‘C’])
};
renderRow(rowData) {
return <Text …>
{rowData}
</Text>
}
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)} />
fetch(url, {
headers: authInfo.header
})
.then((response) => response.json())
.then((responseData) => {
var feedItems = responseData.filter((ev) => ev.type == ‘PushEvent’);
this.setState({
dataSource: this.state.dataSource.cloneWithRows(feedItems)
});
<Image source={{uri: rowData.actor.avatar_url}}
Navigator
<TouchableHighlight
onPress={()=> this.pressRow(rowData)}
underlayColor=’#ddd’
> //cały szablon itemu
</TouchableHighlight>
…
<TabBarIOS.Item …>
<NavigatorIOS …
initialRoute={{
component: Feed,
title: ‘Feed’
}}
pressRow(rowData) {
this.props.navigator.push({
title: ‘Push Event’,
component: PushPayload,
passProps: {
pushEvent: rowData
}
});
}
<ListView
contentInset={{ top: –50 }}
Wywoływanie kodu Objective-C:
Encoding.h:
#include “RTCBridgeModule.h”
@interface Encoding: NSObject <RCTBridgeModule>
@end
Encoding.m:
#include “Encoding.h”
#include “RCTRootView.h”
@implementation Encoding
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(base64Encode: (NSString*)str callback: (RCTResponseSenderBlock)callback)
{
NSData *nsdata = [str dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64Encoded = [nsdata base64EncodedStringWithOptions: 0];
callback(@[base64Encoded]);
}
@end
JS:
var encoding = require(‘NativeModules’).Encoding;
…
encoding.base64Encode(authStr, (encodedAuth) => { … });
Brak komentarzy:
Prześlij komentarz