RN电商项目实战——2.组件的State
任何一个复杂的应用,都是由一个简单的应用发展而来的,当应用功能少的时候可能一个组件足够,但是随着功能的增加,把越来越多的功能放到一个组件中就不方便维护管理了。
React Native 组件的数据分为两种,prop(属性,property的缩写) 和 state(状态) ,无论是属性还是状态的改变,都可能引发组件的重新渲染。
所以,一定要记住一个原则,只能由属性或者状态控制界面显示内容的变化,其它的变量绝对不允许控制显示内容的变化。
归结为一个公式,就像下面这样:
UI=render(data)
公式的含义就是用户看到的界面(UI),应该是render()
函数执行的结果,只接受数据(data)作为参数。这是一个毫无副作用的函数,data其实就是 prop 和 state ,两次函数如果输入的属性和状态相同,得到的 UI 一定是一样的。
prop 一般用于组件的对外接口,state 是组件的内部状态。
为了演示 prop 和 state 的使用,我们创建一个选择商品数量的组件。如下图所示:
我们在工程目录下创建 src 目录,这个目录和 android/ios 目录平级,用来存放 RN 源码,里面创建一个 component 文件夹用来存放自定义组件。
接下来自定义我们第一个组件 Counter ,工程文件路径:“/src/component/Counter.js”
组件中的 state
组件内部进行渲染主要由 state 控制,RN 中记录组件自身数据变化,必须用 state。
设计 Counter 组件显示内容如上图所示,我们可以点击 “+” 和 “-” 按钮改变这个计数,这个变化的数据就要通过 state 控制。
初始化 state
通常在组件类的构造函数结尾处初始化 state,在 Counter 构造函数中,通过对 this.state 的赋值完成了对组件 state 的初始化,参考
下面代码。
export default class Counter extends Component {
// 构造
constructor(props) {
super(props);
// 初始状态
this.state = {
value: 1
};
}
// ...
}
搭建组件布局文件
组件分为左右各一个按钮,中间是一个输入框。实现代码参照下面代码片段:
render() {
return ( // 渲染布局
<View style={styles.operatingBox}>
<TouchableOpacity activeOpacity={0.2}
style={styles.reduce}
onPress={this._reduce.bind(this)}>
<Text allowFontScaling={false} style={[styles.btn1]}>-</Text>
</TouchableOpacity>
<View style={styles.inpBox}>
<TextInput style={styles.inp1}
value={this.state.value.toString()}
autoFocus={false}
underlineColorAndroid="transparent">
</TextInput>
</View>
<TouchableOpacity activeOpacity={0.2}
style={styles.plus}
onPress={this._plus.bind(this)}>
<Text allowFontScaling={false} style={[styles.btn1]}>+</Text>
</TouchableOpacity>
</View>
);
}
_reduce() {
this.setState({
value: this.state.value - 1
})
}
_plus() {
this.setState({
value: this.state.value + 1
})
}
参考上面代码片段,TouchableOpacity 控件按下时有高亮显示,是通用的处理按下效果的组件。
- activeOpacity 属性是指的按下时的透明度,1 代表完全不透明。
- onPress 属性是按钮按下的事件。
- 具体参考文档:http://facebook.github.io/react-native/docs/touchableopacity.html
TouchableOpacity 之外还有 TouchableNativeFeedback 类似组件比较常用,该遵循 Android 质感设计,体验比较好,只不过这个组件只适用于 Android 21版本以上。
有时候我们需要在 Android 21版本上使用 TouchableNativeFeedback,在 iOS 和 Android 低版本上使用 TouchableOpacity。你如果嫌麻烦,可以使用第三方组件 react-native-platform-touchable
地址:https://github.com/react-community/react-native-platform-touchable
TouchableNativeFeedback 只能包裹一个子组件,所以我们有时候为了替换方便,写 TouchableOpacity 时也只包裹一个子组件,所以写完了TouchableOpacity 然后在里面再定义一个相对多余的 View 当容器并不是什么坏习惯。比如前面的代码可以改成:
<TouchableOpacity activeOpacity={0.2}
onPress={this._reduce.bind(this)}>
<View style={styles.reduce}>
<Text allowFontScaling={false} style={[styles.btn1]}>-</Text>
</View>
</TouchableOpacity>
文本控件 Text allowFontScaling={false}
表示不允许控件跟随手机字体大小变化。
TextInput 是输入框组件,underlineColorAndroid="transparent" 是去掉 Android 系统中输入框的下划线。具体参考文档
我们再来看下样式代码,对照着上面代码,里面的组件样式在下面都定义了:
// 样式文件
const styles = StyleSheet.create({
operatingBox: {
width: 120,
height: 35,
borderColor: '#ddd', // 边框颜色
borderWidth: 1,
flexDirection: 'row',//横向布局
alignItems: 'center',
borderRadius: 5, //圆角半径
overflow: 'hidden' // 超出控件范围的隐藏
},
btn1: {
fontSize: 18,
textAlign: 'center',
backgroundColor: 'transparent'
},
inpBox: {
flex: 1,
borderRightWidth: 1,
borderRightColor: '#ddd',
},
reduce: {
width: 34,
height: 34,
justifyContent: 'center',
borderRightWidth: 1,
borderRightColor: '#ddd',
},
plus: {
width: 34,
height: 34,
justifyContent: 'center'
},
inp1: {
flex: 1,
backgroundColor: 'transparent',
textAlign: 'center',
padding: 0,
fontSize: 14
},
});
读取和更新 state 变量
TextInput 组件中的 value 属性定义了输入框的内容,在这里用状态机变量控制显示, this.state.value.toString()
。
TouchableOpacity 组件通过 onPress 绑定了按钮的回调方法,onPress={this._reduce.bind(this)}
这里记得对方法进行绑定操作,否则在方法里无法使用 this 变量。
_reduce()
和 _plus()
方法分别通过 this.setState
函数修改了状态机变量的值。
在代码中,通过 this.state 可以读取到组件的当前 state 。值得注意的是,我们改变组件 state 必须要使用 this.setState 函数,而不能直接去修改 this.state。 当调用this.setState
函数才会驱动组件去更新,界面才会重新渲染。于是当我们点击 “+” 或者 “-” 的时候就可以看到界面的变化。
完善输入框
输入框也可以监听输入内容修改状态机变量,修改之前的代码
<TextInput style={styles.inp1}
returnKeyType='done'
maxLength={3}
onEndEditing={this._checkNumber.bind(this)}
value={this.state.value.toString()}
keyboardType="numeric"
onChangeText={(txt) => this.setState({value:Number(txt)})}
autoFocus={false}
underlineColorAndroid="transparent">
</TextInput>
- keyboardType 约束了软键盘类型,numeric 是数字类型键盘
- returnKeyType 添加了完成的返回按钮
- onChangeText 文本输入框变化的时候回调改方法,在这里修改了状态机变量
- onEndEditing 输入完成的时候回调方法,调用了下面的 _checkNumber 方法。里面对输入内容做了处理,不允许 value 小于 1,并修改了状态机变量。
_checkNumber(){
let value=this.state.value;
if(value===''||value<1){
value=1;
}else{
value=Math.floor(value); //舍去小数
}
this.setState({value:value})
}
虽然每次 setState 每次都刷新界面,大家不用考虑性能的问题,RN 框架都已经处理了。
查看性能方式;
- Android模拟器 Ctrl/Command + M
- iOS 模拟器 Command + D
调出调试菜单,点击 Show Perf Monitor, 如下图所示,当我们点击的时候并没有明显的影响性能,release版本比测试阶段性能消耗会进一步降低。
查看代码
参考React Native电商项目实战——开篇
示例代码可在 GitHub 上获取 https://github.com/yll2wcf/TShop
如果你已经从 GitHub 上克隆了这个程序的 Git 仓库,那么可以执行 git checkout 2a签出程序的这个版本。
大家关注我的公众号—— 于连林 公众号ID: likedev,回复RN 即可查看视频地址。视频每周都会更新,大家保持关注。
读者讨论群
对内容,代码有疑问的读者,可以加入讨论群:274328657 交流,加群务必备注:RN学习。
请大家关注我的简书文集——React Native 电商项目实战 教程都会在这里更新。