Button组件
路径:f8app/js/common/Button.js
Button组件很简单,但是也用到了很多子组件,一个一个来看看
/*f8app/js/common/Button.js*/
'use strict';
var F8Colors = require('F8Colors');//引入颜色常量
var Image = require('Image');//image组件
var LinearGradient = require('react-native-linear-gradient');//渐变色组件,github登录按钮的样式
var React = require('React');
var StyleSheet = require('StyleSheet');
var { Text } = require('F8Text'); //f8app包装的组件,打包了一些和文本有关的子组件
var TouchableOpacity = require('TouchableOpacity');//相当于bootstrap的button组件。
var View = require('View');
class F8Button extends React.Component {
props: { //属性的类型检测
type: 'primary' | 'secondary' | 'bordered';
icon: number;
caption: string;
style: any;
onPress: () => void;
};
render() {
const caption = this.props.caption.toUpperCase();
let icon;
//如果传入图片的属性,则使用这个属性,icon是图标,caption是按钮的文字
if (this.props.icon) {
icon = <Image source={this.props.icon} style={styles.icon} />;
}
let content;
if (this.props.type === 'primary' || this.props.type === undefined) {
content = (
<LinearGradient
start={[0.5, 1]} end={[1, 1]}
colors={['#6A6AD5', '#6F86D9']}
style={[styles.button, styles.primaryButton]}>
{icon}
<Text style={[styles.caption, styles.primaryCaption]}>
{caption}
</Text>
</LinearGradient>
);
} else {
var border = this.props.type === 'bordered' && styles.border;
content = (
<View style={[styles.button, border]}>
{icon}
<Text style={[styles.caption, styles.secondaryCaption]}>
{caption}
</Text>
</View>
);
}
return (
<TouchableOpacity
accessibilityTraits="button"
onPress={this.props.onPress}
activeOpacity={0.8}
style={[styles.container, this.props.style]}>
{content}
</TouchableOpacity>
);
}
}
const HEIGHT = 50;
var styles = StyleSheet.create({
//...看源码,此处省略
});
module.exports = F8Button;
F8Text组件
/*f8app/js/common/F8Text.js*/
'use strict';
import React, {StyleSheet, Dimensions} from 'react-native';
import F8Colors from 'F8Colors';
//封装text
export function Text({style, ...props}: Object): ReactElement {
return <React.Text style={[styles.font, style]} {...props} />;
}
//封装标题
export function Heading1({style, ...props}: Object): ReactElement {
return <React.Text style={[styles.font, styles.h1, style]} {...props} />;
}
//段落内容主体
export function Paragraph({style, ...props}: Object): ReactElement {
return <React.Text style={[styles.font, styles.p, style]} {...props} />;
}
//使用Dimensions组件获取实际硬件的宽度
const scale = Dimensions.get('window').width / 375;
//根据实际硬件宽度放大得到实际尺寸的动态缩放
function normalize(size: number): number {
return Math.round(scale * size);
}
const styles = StyleSheet.create({
h1: {
fontSize: normalize(24), //normalize函数的使用。
lineHeight: normalize(27),
color: F8Colors.darkText,
fontWeight: 'bold',
letterSpacing: -1,
}
//省略部分代码
});
TouchableOpacity 组件
封装了ios和android不同的类型
/*f8app/js/common/F8Touchable.js*/
'use strict';
import React, {
TouchableHighlight,
TouchableNativeFeedback,
Platform,
} from 'react-native';
function F8TouchableIOS(props: Object): ReactElement {
return (
<TouchableHighlight
accessibilityTraits="button"
underlayColor="#3C5EAE"
{...props}
/>
);
}
//根据动态或的的操作系统加载不同的组件
const F8Touchable = Platform.OS === 'android'
? TouchableNativeFeedback
: F8TouchableIOS;
module.exports = F8Touchable;
以上几个组件LoginButton组件中使用.
关键的几个地方:根据状态加载组件的文字,es6的异步竞争操作,redux的connect函数的使用。
/*f8app/js/common/LoginButton.js*/
'use strict';
const React = require('react-native');
const {StyleSheet} = React;
const F8Button = require('F8Button');
const { logInWithFacebook } = require('../actions'); //loginbutton要dispatch的函数
const {connect} = require('react-redux'); //connect函数
class LoginButton extends React.Component {
props: {
style: any;
source?: string; // For Analytics
dispatch: (action: any) => Promise;
onLoggedIn: ?() => void;
};
state: {
isLoading: boolean;
};
_isMounted: boolean;
constructor() {
super();
this.state = { isLoading: false };
}
componentDidMount() {
this._isMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
//根据isLoading的状态决定加载那个组件
if (this.state.isLoading) {
return (
<F8Button
style={[styles.button, this.props.style]}
caption="Please wait..."
/>
);
}
return (
<F8Button
style={[styles.button, this.props.style]}
icon={require('../login/img/f-logo.png')}
caption="Log in with Facebook"
onPress={() => this.logIn()}
/>
);
}
//登录的异步操作
async logIn() {
const {dispatch, onLoggedIn} = this.props; //通过connect注入的dispatch和onLOggedIN函数
this.setState({isLoading: true});//点击登录按钮,改变isLoading的状态
try {
await Promise.race([ //下面两个异步函数式竞争关系,第一个在1.5秒没完成就会执行超时函数tiemout
dispatch(logInWithFacebook(this.props.source)),//dispatch登录函数
timeout(15000),//超时函数
]);
} catch (e) {
const message = e.message || e;
if (message !== 'Timed out' && message !== 'Canceled by user') {
alert(message);
console.warn(e);
}
return;
} finally {
this._isMounted && this.setState({isLoading: false});
}
onLoggedIn && onLoggedIn();
}
}
async function timeout(ms: number): Promise {
return new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('Timed out')), ms);
});
}
var styles = StyleSheet.create({
button: {
alignSelf: 'center',
width: 270,
},
});
//connect应该是redux学习的一个难点和突破点,UI组件通过connect
//可以获取全局的所有的state,redux里面只有一个state树。但是
//LoginButton的状态是自己决定的,因此没有注入state
//redux还可接受UI组件的dispatch函数传递的action和相应的实参,
//如果这里的action和redux的actiontype想匹配就就导致相应的State的改变。
//这个组件state应该返回登录的token供其他组件来使用。
module.exports = connect()(LoginButton);