从零学React Native之02状态机
本篇文章首发于简书 欢迎关注
之前我们介绍了RN相关的知识: 
是时候了解React Native了 
从零学React Native之01创建第一个程序
本篇文章主要介绍下下面的知识: 
1.简单界面的搭建 
2.状态机添加 
3.渲染框架简介 
4.语法简化
搭建界面
之前我们介绍了如何创建一个应用,现在我们来开发一个简单的注册界面。注释都写在代码里了, 如下:
 
进入项目目录下,修改下index.android.js(开发IOS程序修改index.ios.js)
import React, { Component } from 'react';
import {
    AppRegistry, //框架提供的API
    StyleSheet,
    Text,  // RN提供的组件
    View,
    TextInput // 记得引入组件
} from 'react-native';
//let是更完美的var
let Dimensions = require('Dimensions');// 宽高
let totalWidth = Dimensions.get('window').width;  //声明了三个变量,根据屏幕动态变化
let leftStartPoint = totalWidth * 0.1;
let componentWidth = totalWidth * 0.8;
class AwesomeProject extends Component {
     render() {
        return (
            // 绘制渲染的控件
            <View style={styles.container}>
                <TextInput style={styles.numberInputStyle}
                           placeholder={'请输入手机号'}/>
                <Text style={styles.textPromptStyle}>
                    您输入的手机号:
                </Text>
                <TextInput style={styles.passwordInputStyle}
                           placeholder={'请输入密码'}
                           secureTextEntry={true}/>
                <Text style={styles.bigTextPrompt}>
                    确  定
                </Text>
            </View>
        );
    }
}
// 样式  const变量只能在声明的时候赋值一次
const styles = StyleSheet.create({
    //各个组件都没有定义高度,父View设置了flex1,他会沾满整个高度,子组件没有设置会包裹内容
    container: {
        flex: 1,  //表示宽高会自动扩展
        backgroundColor: 'white'
    },
    numberInputStyle: {
        top: 20,     // top left表示从父组件的顶端(左侧) 向下(向右) 多少位置显示
        left: leftStartPoint,
        // height:30,  // IOS开发需要加上该高度
        width: componentWidth,
        backgroundColor: 'gray',
        fontSize: 20
    },
    textPromptStyle: {
        top: 30,
        left: leftStartPoint,
        //  // height:30,  // IOS开发需要加上该高度 因为IOS中TextInput不会自动设置高度
        width: componentWidth,
        fontSize: 20
    },
    passwordInputStyle: {
        top: 50,
        left: leftStartPoint,
        width: componentWidth,
        backgroundColor: 'gray',
        fontSize: 20
    },
    bigTextPrompt:{
        top:70,
        left:leftStartPoint,
        width:componentWidth,
        backgroundColor:'gray',
        color:'white',
        textAlign:'center',//位置居中显示
        fontSize:60
    }
});
// 注册了 AwesomeProject 组件
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);
在上面的代码里, AwesomeProject和创建的项目的名称一样, 在这里代表创建的一个组件, 是继承自Component,React Native 每个组件都具备render()方法,该方法用来绘制UI, 这个方法直接return了一个JSX代码描述的UI组件。 
const styles定义了组件的样式.
当一个组件通过AppRegistry API向React Native注册后,React Native框架将把这个组件渲染到手机屏幕上,并在发生UI事件时,将相应的UI事件通知给组件.这样,代码就能进行UI处理与相应的业务逻辑处理了. 
大家也不难猜到,当React Native框架渲染一个组件到手机屏幕时,会调用该组件的render函数,并按render函数返回的JSX代码描述的可渲染元素来渲染手机UI界面.
添加状态机
接下来我们来处理下输入框了,如果你做过Android或者IOS开发, 对比下React Native,你会发现RN的设计思维和这俩不一样. 
React框架将所有的UI视为一个简单的状态机,那么任意一个UI场景就是状态机中的一种状态. 
状态机的值一旦变化, 默认界面就会重新渲染,这样一个过程可以很轻松的做到数据与UI保持一致.我们只需要空间状态机中的值就行.
修改代码:
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */
import React, { Component } from 'react';
import {
    AppRegistry, //框架提供的API
    StyleSheet,
    Text,  // RN提供的组件
    View,
    TextInput // 记得引入组件
} from 'react-native';
//let是更完美的var
let Dimensions = require('Dimensions');// 宽高
let totalWidth = Dimensions.get('window').width;  //声明了三个变量,根据屏幕动态变化
let leftStartPoint = totalWidth * 0.1;
let componentWidth = totalWidth * 0.8;
class AwesomeProject extends Component {
    //构造函数, 组件创建的时候会执行
    constructor(props) {
        super(props); //必须有这句代码 父组件向子组件传递属性, 比如styles属性等
        // 声明状态机的初始值
        this.state = {
            inputedNum: '',
            inputedPw: ''
        };
    }
    // 定义函数
    updateNum(newText) {
        this.setState((state)=> {
            return {
                inputedNum: newText
            }
        });
    }
    // 定义函数
    updatePW(newText) {
        this.setState(()=> { // 用不到的参数也可以不用写
            return {
                inputedPw: newText
            }
        });
    }
    // 绘制渲染的控件
    render() {
        return (
        /*(newText)=>this.updateNum(newText)
         它将收到的字符串为参数调用当前组件的updateNum函数,并且将updateNum函数的返回值返回
         当前函数在输入框文本变化的时候会调用
         语句可以改成
         this.updateNum
         但一定不要写成
         this.updateNum(newText) 因为有右箭头函数的时候newText是形式参数
         没有箭头函数的时,newText就没有定义
         */
            <View style={styles.container}>
                <TextInput style={styles.numberInputStyle}
                           keyboardType={'phone-pad'}
                           placeholder={'请输入手机号'}
                           onChangeText={(newText)=>this.updateNum(newText)}/>
                <Text style={styles.textPromptStyle}>
                    您输入的手机号:{this.state.inputedNum}
                </Text>
                <TextInput secureTextEntry={true}
                           style={styles.passwordInputStyle}
                           placeholder='请输入密码'
                           onChangeText={(newText)=>this.updatePW(newText)}/>
                <Text style={styles.bigTextPrompt}>
                    确 定
                </Text>
            </View>
        );
    }
}
// 样式  const变量只能在声明的时候赋值一次
const styles = StyleSheet.create({
    //各个组件都没有定义高度,父View设置了flex1,他会沾满整个高度,子组件没有设置会包裹内容
    container: {
        flex: 1,  //表示宽高会自动扩展
        backgroundColor: 'white'
    },
    numberInputStyle: {
        top: 20,     // top left表示从父组件的顶端(左侧) 向下(向右) 多少位置显示
        left: leftStartPoint,
        // height:30,  // IOS开发需要加上该高度
        width: componentWidth,
        backgroundColor: 'gray',
        fontSize: 20
    },
    textPromptStyle: {
        top: 30,
        left: leftStartPoint,
        //  // height:30,  // IOS开发需要加上该高度 因为IOS中TextInput不会自动设置高度
        width: componentWidth,
        fontSize: 20
    },
    passwordInputStyle: {
        top: 50,
        left: leftStartPoint,
        width: componentWidth,
        backgroundColor: 'gray',
        fontSize: 20
    },
    bigTextPrompt: {
        top: 70,
        left: leftStartPoint,
        width: componentWidth,
        backgroundColor: 'gray',
        color: 'white',
        textAlign: 'center',//位置居中显示
        fontSize: 60
    }
});
// 注册了 AwesomeProject 组件
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);
其中构造函数中初始化了状态机的值, 然后在组件中声明了两个方法用来修改状态机的值, 在TextInput中onChangeText时 调用该方法, 说白了就是当用户输入的时候状态机的值就会变化.
当需要改变状态机变量的值时, 一定要并且只能使用this.setState函数,永远不要出现”this.state.某状态机变量名=某值” 这样的语句.这样的语句从JS语法上来说是合法的, 站在RN角度上就是不合法的.
RN开发的一个原则是努力让自己定义的RN组件成为无状态的RN组件,或者说在不影响程序结构的情况下,尽可能减少所有状态的RN组件数目.
这句话新手可能不太理解, 说白了就是本来可以不用状态机变量就可以实现的功能就不要用, 因为状态机变量的改变就会导致界面重新刷新.浪费性能.
哪些是必须有的状态机变量呢? 比如文本输入,摄像头拍照等等,这些都必须依赖状态机刷新界面.
一个好的React Native设计思路是: 创建多个只负责渲染数据的无状态React Native组件,将它们封装在一个有状态的React Native组件中,并把这个有状态的React Native组件的状态机变量的值通过props传给无状态的React Native组件. 这种思路后面的文章会专门介绍.
千万不要把一个React Native组件放在状态机变量中. 正确的做法是把它放在render函数中,让它成为本RN组件的子组件
充分了解状态机
我们再来充分了解下状态机, 修改状态机可以使用setState函数, 其实RN还提供了另外两个函数, replaceState函数与forceUpdate函数,但是不建议大家用. 
setState函数的原型是:
setState(object oldState,function callback)RN会根据哪个状态机值变化,从而去做相应的渲染,如果setState函数将传入函数的返回值与当前状态机相比没有任何修改与增加,那么将不会进行渲染.
this.setState函数提供的第一个参数是之前的状态,提供第二个可选参数——一个回调函数,它将在setState完成并且重新渲染完成后调用. 
我们来修改下updateNum函数并加入一个新的函数changeNumDone作为setState的第二个参数.
    updateNum(newText) {
        this.setState((state)=> {
            for(var aName in state){  // 遍历之前的状态的 键和值
                console.log(aName);
                console.log(state[aName]);
            }
            return {
                // 改变状态机变量
                inputedNum: newText,
                aBrandnewStateVariable:'I am a new variable.' //添加了新的状态机变量
            };
        },this.changeNumDone); // setSate函数的回调函数, 当设置完成重新渲染完成后调用
    }
    changeNumDone(){
        console.log('React Native has changed inputed Num');
    }当我们在TextInput中分别输入 1 和 3  在chrome中调试来看看输出的日志 
可以看到 输入1的时候 之前的状态机里面没有值, 这时候我们在return中添加了一个新的状态机的键值对——aBrandnewStateVariable, changeNumDone函数在之后调用,当输入3的时候就把之前状态机的键值对输出了.
判断是否渲染
如果你有些特殊需求,不想让状态机的值一变化就刷新,RN也提供了另一个方法
boolean shouldComponentUpdate(object nextProps,object nextState)如果在组件中实现这个函数,当RN决定重新渲染组件时,会先调用这个函数.如果返回false, RN将放弃渲染组件. 方法里面接收两个参数,分别代表接下里准备进行渲染所基于的props与state. props里面就包含的styles属性等等.
在当前组件中实现下面代码,看到的效果就是当输入的电话号码不超过3位的时候不会渲染.
    // 接下来是否进行渲染 shouldComponentUpdate(object nextProps, object nextState)
    shouldComponentUpdate() { //接下来是否进行渲染
        if(this.state.inputedNum.length<3) return false;
        return true;
    }简化写法
有些方法还可以简化, 比如下面的函数就可以简化一下
    //updatePW(newText) {
    //    this.setState(()=> { // 用不到的参数也可以不用写
    //        return {
    //            inputedPw: newText
    //        }
    //    });
    //}
    // 简化写法
    updatePW(inputedPw) {
        this.setState({inputedPw});
    }甚至可以不定义上面的函数 直接在onChangeText中按照下面的写法, 实现的效果和上面一样,
onChangeText={(inputedPw)=>this.setState({inputedPw})更多精彩请关注微信公众账号likeDev,公众账号名称:爱上Android。
从零学React Native之02状态机的更多相关文章
- 从零学React Native之03页面导航
		之前我们介绍了RN相关的知识: 是时候了解React Native了 从零学React Native之01创建第一个程序 从零学React Native之02状态机 本篇主要介绍页面导航 上一篇文章给 ... 
- 从零学React Native之13 持久化存储
		数据持久化就是指应用程序将某些数据存储在手机存储空间中. 借助native存储 这种方式不言而喻,就是把内容传递给native层,通过原生API存储,详见从零学React Native之05混合开发 ... 
- 从零学React Native之11 TextInput
		TextInput 组件是用来通过键盘输入文字,可以使用View组件和Text组件样式,没有自己特定的样式. 与Text组件类似,TextInput组件内部的元素不再使用FlexBox布局,而采用文本 ... 
- 从零学React Native之12 组件的生命周期
		一个React Native组件从它被加载,到最终被卸载会经历一个完整的生命周期.所谓生命周期,就是一个对象从开始生成到最后消亡所经历的状态,理解生命周期,是合理开发的关键. ES6语法和之前的ES5 ... 
- 从零学React Native之04自定义对话框
		本篇主要介绍: 1. 自定义组件 2. Alert 对话框 自定义对话框 之前的我都是利用React Native提供的基础组件对它们进行排列组合, 其实自定义也很简单, 我们还是拿上一篇文章的例子进 ... 
- 从零学React Native之14 网络请求
		通过HTTP或者HTTPS协议与网络侧服务器交换数据是移动应用中常见的通信方式. node-fetch是RN推荐的请求方式. React Native框架在初始化项目时, 引入了node-fetch包 ... 
- 从零学React Native之10Text
		在React Native开发中,所有需要显示的字符串文本都需要放置在Text或者Text的子组件中.虽然在之前的文章中多次使用了Text组件,但是Text组件还是值得专门学习的, 并没有想象中的那么 ... 
- 从零学React Native之08Image组件
		开发过程中, 几乎每个项目都会用到图片. RN就是通过Image组件显示图片.既可以加载网络图片,也可以加载本地资源图片. Image组件必须在样式中声明图片的款和高.如果没有声明,则图片将不会被呈现 ... 
- 从零学React Native之07View
		View 组件是React Native最基本的组件.绝大部分其他React Native 组件. View组件的颜色和边框 backgroundColor 键用来指定颜色. RN 0.19版本开始, ... 
随机推荐
- 使用Charles在iOS6上进行抓包
			抓取Web页面的网络请求很容易,Chrome和Firefox都很容易做到.iOS APP如何抓包呢?其实也很容易,我比较喜欢使用 Charles. 我用的是Mac电脑,首先建立一个热点,然后让iOS设 ... 
- COOK50小结
			题目链接 很遗憾.看到第五题的通过人数就不敢做了.待日后补上. A题 求最长的连续子序列,使得他们满足gcd为1. 如果有相邻的两个数的gcd为1,那么整个序列的gcd值也就是1, 否则就是该序列不存 ... 
- What every computer science major should know 每一个计算机科学专业的毕业生都应该都知道的
			Given the expansive growth in the field, it's become challenging to discern what belongs in a modern ... 
- 文本流向 layout-flow
			作用与语法 文本流向layout-flow用来定义网页中的文本流向方式. 即排列方式,主要有两个属性,分别是horizonta (水平的) 和vertical-ideographic (垂直的). 文 ... 
- P5562 [Celeste-B]Center of the Earth 题解
			构造 因为题目只要求两位相同,所以可以暴力枚举这两位所有的可能性,方案数为\(O(n^2)\). 但是,这么做是显然不优的,因为完全没有用到第三位. 观察题目条件:n为偶数. 就想一想能不能奇数偶数分 ... 
- 用蒙特卡罗方法解非线性规划MATLAB
			共需要三个M文件,主程序为randlp.m randlp.m: function [sol,r1,r2]=randlp(a,b,n) %随机模拟解非线性规划 debug=1; a=0; %试验点下界 ... 
- React高阶组件 和 Render Props
			高阶组件 本质 本质是函数,将组件作为接收参数,返回一个新的组件.HOC本身不是React API,是一种基于React组合的特而形成的设计模式. 解决的问题(作用) 一句话概括:功能的复用,减少代码 ... 
- 使用 store 来优化 React 组件
			在使用 React 编写组件的时候,我们常常会碰到两个不同的组件之间需要共享状态情况,而通常的做法就是提升状态到父组件.但是这样做会有一个问题,就是尽管只有两个组件需要这个状态,但是因为把状态提到了父 ... 
- umount报错解决device is busy
			umount –a 报错device is busy如图 df -h 执行 umount -l /dev/sdk1 fuser -m -v /cache10 再查看卸载了 
- java视频长度读取 方案参照文件
			ffmpeg库 必须http://ffmpeg.org/download.html#build-windows 第三方jar sauronsoftwarehttp://www.sauronsoftwa ... 
