RN-笔记

作者: 壹点微尘 | 来源:发表于2017-07-06 11:28 被阅读346次
    • 创建指定RN版本的工程
    react-native init MyApp --version 0.44.3。注意版本号必须精确到两个小数点。
    
    • 倒计时 (系统自带的)
    //定时器: 隔2s切换到Main
    setTimeout( ()=>{
                //页面的切换
                this.props.navigator.replace({
                    component:Main, //具体路由的版块
                });
     } ,2000);
    
    • 按钮 Switch的状态切换
    //!this.state.isOn 取反,切换状态
    // onValueChange={()=>{this.setState({isOn:!this.state.isOn})}}
    <Switch value={this.state.isOn === true} onValueChange={()=>{this.setState({isOn:!this.state.isOn})}} style={{marginRight:8}}/>
    
    • ScrollView的吸顶效果
     <ScrollView
              style={styles.scrollViewStyle}
              contentInset={{top:-200}}
              contentOffset = {{y:200}}
     >
    </ScrollView>
    
    吸顶效果
    • 遍历时,控件一定要加 key={i}
        //页码指示器
        renderIndicator(){
            //指示器数组
            var indicatorArr = [], style;
            //遍历创建组件
            for (var i=0;i<2;i++){
                //设置圆点样式
                style = (i === this.state.activePage) ? {color:'orange'} : {color:'gray'}
                indicatorArr.push(
                    //                  一个数组里面有多个样式
                    <Text key={i} style={[style,{fontSize:30}]}>•</Text>
                );
            }
            return indicatorArr;
        }
    
    • 用ListView实现九宫格布局
      通常情况下,我们对ListView的操作是纵向的,如果是横向的,则需要设置ListView的contentContainerStyle属性,添加flexDirection:‘row’让多个ListView在同一行显示,而且通过flexWrap:'wrap'进行换行。
        contentViewStyle:{
            //设置主轴的方向
            flexDirection:'row',
            //多个cell在同一行显示
            flexWrap:'wrap',
            //宽度
            width:Dimensions.get('window'),
        },
    
    全局变量
    var {width} = Dimensions.get('window');
    //全局的变量
    var cols = 5;
    var cellW = Platform.OS == 'ios' ? 70 : 50;
    var cellH =  70;
    var vMargin = (width-cellW*cols) / (cols + 1);
    
    渲染
    render(){
            return(
                <ListView
                    dataSource = {this.state.dataSource}
                    renderRow = {this._renderRow}
                    contentContainerStyle = {styles.contentViewStyle}
                    scrollEnabled = {false}
                />
            );
        },
    
    样式
    const styles = StyleSheet.create({
        contentViewStyle:{
            //设置主轴的方向
            flexDirection:'row',
            //多个cell在同一行显示
            flexWrap:'wrap',
            //宽度
            width:width,
        },
        cellStyle:{
            width:cellW,
            height:cellH,
            justifyContent:'center',
            alignItems:'center',
            marginTop:10,
            marginLeft:vMargin,
        },
        titleStyle:{
            color:'gray',
            fontSize:Platform.OS == 'ios' ? 14: 10
        }
    });
    
    效果
    美团滚动菜单
    • 替换字符串的内容

    下面的图片url中,包含有/w.h/字符,需要前端传递给后台具体的尺寸,然后返回对应尺寸的图片

    "imageurl": "http://p0.meituan.net/w.h/groupop/b8fb2def2c0063c9acabed6cbf1c65449452.png"
    

    js中的做法

        //处理图像的尺寸
        dealWithImgUrl(url){
            //url.search('w.h') 如果找到,则返回正数;没有找到,则返回 -1;
            if (url.search('w.h') == -1){
                //没有找到, 正常返回
                return url;
            }else {
                //把'w.h'替换成具体的尺寸
                return url.replace('w.h',64.43);
            }
        }
    
    • 对象序列化: JSON.stringify(result)
    var result = {'name':'张三','age':'25'}
    //对象序列化
    JSON.stringify(result) 
    
    • 解析一个JSON字符串 : JSON.parse()
    resolve(JSON.parse(result));
    一般用作AsyncStorage等获取的是json字符串格式的数据,需要转换成Json格式
    

    一般用作fetch数据请求中

     post(url, data) {
            fetch(url, {
                method: 'POST',
                headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json',
                },
                //将传递过来的参数对象序列化
                body: JSON.stringify(data)
            })
                .then(response=>response.json())
                .then(result=> {
                    this.setState({
                        //转换成了json字符串格式,在实际开发中不用转,直接可以用
                        //该界面主要用作通过<Text>标签把请求结果显示出来
                        result: JSON.stringify(result)
                    })
                })
                .catch(error=> {
                    this.setState({
                        result: JSON.stringify(error)
                    })
                })
        }
    
    • 设置阴影

    elevation number(限Android)使用Android原生的 elevation API来设置视图的高度(elevation)。这样可以为视图添加一个投影,并且会影响视图层叠的顺序。此属性仅支持Android5.0及以上版本。

            //阴影安卓和iOS有别:
            //IOS
            shadowColor:'red',
            shadowOffset:{width:1,height:1},
            shadowOpacity:0.4,
            shadowRadius:1,
            //安卓
            elevation:2,
    
    • try catch 抛出异常
    fetch(){
    
            return new Promise((resolve,reject)=>{
    
                AsyncStorage.getItem(this.flag,(error,result)=>{
    
                    if (error){
                        reject(error);
                    }else {
                        if (result){
                            //解析有可能会出错,抛出异常
                            try {
                                //转成JSON格式
                                resolve(JSON.parse(result));
                            } catch (e){
                                //出错
                                reject(e);
                            }
                        }
                    }
                })
            })
        }
    
    • 使用map遍历
    this.state.languages.map((result,i,array)=>{
           let lan = array[i];
           return lan.checked ? <PopularTab tabLabel={lan.name}>{lan.name}</PopularTab> : null;
    })
    
    • 获取数组中元素的索引
    let index = this.dataArray.indexOf(item);
    
    • DeviceEventEmitter 事件儿发射器]

    添加通知

       componentDidMount(){
            this.listener=DeviceEventEmitter.addListener('showToast',(text)=>{
               this.toast.show(text,DURATION.LENGTH_LONG);
            });
        }
    

    移除通知

        componentWillUnmount(){
            this.listener&&this.listener.remove();
        }
    

    发送通知 (在需要发送通知的地方发送通知)

    DeviceEventEmitter.emit('showToast','显示缓存数据');
    
    • TextInput 注意

    默认给TextInput设置要显示的内容,如果设置value='xxx',会导致输入框无法输入!

    <TextInput
          style={styles.input}
          value={URL}
          onChangeText={text=>this.text=text}
    />
    

    应设置defaultValue='xxx',这样就可以正常输入了

    <TextInput
          style={styles.input}
          defaultValue={URL}
          onChangeText={text=>this.text=text}
    />
    
    • react-native-htmlview的使用
    //用p标签包括以下,也可以换用别的标签
    let description = '<p>'+this.props.data.description+'</p>';
    <HTMLView
           value={description}
           stylesheet={{ //设置标签的样式
               p:styles.description,
               a:styles.description,
            }}
           onLinkPress={(url) => {}}
    />
    
    const styles = StyleSheet.create({
        description:{
            fontSize:14,
            marginBottom:2,
            color:'#757575',
        },
    })
    
    • ES6和ES5的不一样
    export default class CustomKeyPage extends Component {
        constructor(props) {
            super(props);
           //可以往里面存东西,但是不会触发更细状态机
            this.changeValues = [];
           //会触发更新状态机
            this.state={
                dataArray:[],
            }
        }
    }
    
    • 报错: React.Children.only expected to receive a single React element child.

    则需要用View把这些组件包裹起来

    <View style={{flexDirection:'row'}}>
           <Image source={require('./images/ic_sort.png')}/>
           <Text>{this.props.data.name}</Text>
    </View>
    
    • undefined is not an object (evaluating 'this.props.navigator.push')

    报这样的错误,需要检查下各个界面的延展属性有木有传!!!

    • this.setState is not a function. (In 'this.setState({ isVisible: false})', 'this.setState' is undefined)

    有时候第三方插件采用的ES5的写法,导致程序崩溃,先检查方法函数是否采用的ES5,再改成ES6.

    • 按钮状态的切换

    设置this.state

    this.state={
             //默认未选中
             isFavorite:false,   
             //默认图片
             favoriteIcon:require('../../res/images/ic_unstar_transparent.png'),
    }
    

    创建按钮

            let favoriteButton = <TouchableOpacity
                onPress={()=>this.onPressFavorite()}
            >
                <Image
                    style={{width:22,height:22,tintColor:'#2196F3'}}
                    source={this.state.favoriteIcon}
                />
            </TouchableOpacity>
    

    按钮点击,状态切换

        onPressFavorite(){
            this.setState({
                //this.state.isFavorite状态取反
                isFavorite:!this.state.isFavorite,
                //显示图片切换
                favoriteIcon:!this.state.isFavorite ? require('../../res/images/ic_star.png'):require('../../res/images/ic_unstar_transparent.png'),
            });
        }
    
    按钮状态切换
    • 当props发生变化时执行,初始化render时不执行

    当props发生变化时执行,初始化render时不执行,在这个回调函数里面,你可以根据属性的变化,通过调用this.setState()来更新你的组件状态,旧的属性还是可以通过this.props来获取,这里调用更新状态是安全的,并不会触发额外的render调用

        //当数据状态不能及时刷新时,调用此方法
        componentWillReceiveProps(nextProps){
            this.setFavoriteState(nextProps.projectModel.isFavorite);
        }
    
        setFavoriteState(isFavorite){
            this.setState({
                isFavorite:isFavorite,
                favoriteIcon:isFavorite ? require('../../res/images/ic_star.png'):require('../../res/images/ic_unstar_transparent.png'),
            });
        }
    
    • item.id.toString() 转成字符串
    • 事件的点击等操作方法前加on
    //onPress={()=>this.onRightButtonClick()},onRightButtonClick前面加on
    <TouchableOpacity
            onPress={()=>this.onRightButtonClick()}
    >我是按钮</TouchableOpacity>
    

    方法实现

    onRightButtonClick(){
            alert('我被点击了');
    }
    
    • multiGet ====> AsyncStorage 通过多个key获取数据
    //参数 keys:数组
    AsyncStorage.multiGet(keys,(err,stores)=>{
                            try {
                                stores.map((result,i,store)=>{
                                    let value = store[i][1];
                                    if (value) items.push(value);
                                })
                                resolve(items);
                            }catch (e){
                                reject(e);
                            }
                        });
    
    • 通知的使用

    监听页面

       //添加通知
       componentDidMount() {
            this.listener = DeviceEventEmitter.addListener('favoriteChanged_popular', () => {
                this.isFavoriteChanged=true;
            });
            this.loadData();
        }
        //记得移除通知
        componentWillUnmount(){
            if (this.listener) {
                this.listener.remove();
            }
        }
    

    在需要发送通知的地方发送通知

     DeviceEventEmitter.emit('favoriteChanged_popular');
    
    • 定义一些常量
    定义一些常量
    /**
     * 定一些常量
     * 更多菜单
     * */
    
    //导出标识
    export const MORE_MENU={
        Custom_Language:'Custom Language',
        Sort_Language:'Sort Language',
        Custom_Key:'Custom Key',
        Sort_key:'Sort Key',
        Remove_Key:'Remove Key',
        About_Author:'About Author',
        About:'About',
    
    }
    

    在使用的地方这样引入

    import {MORE_MENU} from '../../common/MoreMenu'
    

    这样使用

    //直接调用
    MORE_MENU.About
    //如下面示例
    <TouchableHighlight
              onPress={()=>this.onClick(MORE_MENU.About)}
    >我是按钮</TouchableHighlight>
    
    • 定义全局的样式类
    定义一个全局的样式

    代码如下:

    /**
     * 全局样式
     * @flow
     */
    import {
        Dimensions
    }from 'react-native'
    const {height, width} = Dimensions.get('window');
    
    //导出全局样式
    module.exports ={
        line: {
            height: 0.4,
            opacity:0.5,
            backgroundColor: 'darkgray',
        },
        root_container:{
            flex: 1,
            backgroundColor: '#f3f3f4',
        },
        backgroundColor: '#f3f3f4',
        nav_bar_height_ios:44,
        nav_bar_height_android:50,
        window_height:height,
    };
    

    使用的地方,先导入

    import GlobalStyles from '../../../res/styles/GlobalStyles'
    

    调用方法:

    <View style={GlobalStyles.line}></View>
    
    • 公共类里面定义表示,用于区分不同的组件调用
    定义标识
    在别的类中,调用该组件
    调用该实例的render()方法
    • Linking 打开浏览器、邮箱或者其它的应用

    如果想在打开链接前先检查是否安装了对应的应用,则调用以下方法:

    //以发送邮箱为例
    //打开邮箱,由于模拟器上没有安装邮箱,故无法打开
    var url='mailto://1047912930@qq.com';
    Linking.canOpenURL(url).then(supported => {
      if (!supported) {
        console.log('Can\'t handle url: ' + url);
      } else {
        return Linking.openURL(url);
      }
    }).catch(err => console.error('An error occurred', err));
    
    • Map()函数
    let map = new Map([
      [1, 'one'],
      [2, 'two'],
      [3, 'three'],
    ]);
    let arr = [...map.keys()]; // [1, 2, 3]
    

    具体使用如下


    实例化一个itemMap 添加k,v
    • async和await用法

    要理解async和await的用法,首先要了解Task相关知识,这里不做说明,因为这不是本文的重点。
    如果你已经对Task很了解,那么如何使用async和await,在此主要总结了以下三点:
    1.只有在async方法里面才能使用await操作符;
    2.await操作符是针对Task对象的;
    3.当方法A调用方法B,方法B方法体内又通过await调用方法C时,如果方法C内部有异步操作,则方法B会等待异步操作执行完,才往下执行;但方法A可以继续往下执行,不用再等待B方法执行完。
    使用如下:(省去了链式调用的繁琐)

        async updateFavorite(repositories){
            if (repositories) this.repositories = repositories;
            //等于空  直接return
            if (!this.repositories) return;
            if (!this.favoriteKeys){
                this.favoriteKeys = await this.favoriteDao.getFavoriteKeys();
            }
        }
    
    • 设置this.state要注意
    constructor(props) {
            super(props);
           this.state={
                //默认值要为数组[],或者对象{},千万不能是null,(null会报错)
                dataSource:[],
            }
        }
    
    • StackNavigator 导航跳转
    const { navigate } = this.props.navigation;
    //点击调转
    onPress={() => navigate('Detail', {
            //传值
            id: item.id,
            //回调
            callback: (data) => {
               this.setState({
                   childState: data
                    })
                }
    })}
    

    Detail页面显示

      static navigationOptions = {
        title: '详情页',
      };
    

    没有回调 => 返回

    onPress={()=>this.props.navigation.goBack()}
    

    带有参数=>返回

    const {state,goBack} = this.props.navigation;
    <Text onPress={()=>{
                 state.params.callback('假装是回传的内容');
                 goBack();
    
                   }}
    >返回</Text>
    
    • AsyncStorage存储 只能存 "死的"字符串 (json字符串)
    async componentDidMount() {
        const { state: { params: { id } } } = this.props.navigation;
        let textData, jsonData;
        //await 该函数执行完 才能往下接着执行
        //如果使用await componentDidMount()方法前要加 async
        textData = await AsyncStorage.getItem(id);
    
        if (textData) {
          // alert('数据来自本地');
        } else {
          const rawData = await fetch(`${api}/${id}`);
          textData = await rawData.text();
          // alert('数据来自服务器');
        }
        // title, summary
        // "images": {
        //   "small": "http://img3.doubanio.com/view/movie_poster_cover/ipst/public/p494268647.webp",
        //     "large": "http://img3.doubanio.com/view/movie_poster_cover/lpst/public/p494268647.webp",
        //     "medium": "http://img3.doubanio.com/view/movie_poster_cover/spst/public/p494268647.webp"
        // }
        // 反序列化: "死的"字符串 => "活的"对象
        jsonData = JSON.parse(textData);
        jsonData.image = jsonData.images.large.replace('webp', 'jpg');
    
        // 序列化: "活的"对象 => "死的"字符串
        // const textData = JSON.stringify(jsonData);
        AsyncStorage.setItem(id, textData);
    
        this.setState({
          data: jsonData,
          ready: true,
        });
    
        this.fetchVideo(jsonData.mobile_url);
      }
    
    • 复制到剪切板
    Clipboard.setString('xxx');
    
    • 打开邮箱
    Linking.openURL('mailto://1047912930@qq.com');
    
    • 给textInput设置paddingLeft:5,输入框的光标就会往右移动5px的距离
    textInput: {
            flex: 1,
            paddingLeft: 5,//输入框的光标就会往右移动
        },
    
    • 让键盘隐藏(输入框失去焦点)

    处理方式(一):
    先定义该变量 dismissKeyboard

    const {width, height} = Dimensions.get('window');       // 获取屏幕尺寸
    const dismissKeyboard = require('dismissKeyboard');     // 获取键盘回收方法
    // 第三方
    import {PullList} from 'react-native-pull';
    

    调用

      // 返回
        pop() {
            // 回收键盘
            dismissKeyboard();
            this.props.navigator.pop();
        }
    

    处理方式(二):

    先给输入框Input设置ref
    <TextInput
         ref="input"
         onChangeText={text=>this.inputKeky=text} //将text赋值给this.inputKeky
         style={styles.textInput}
    > </TextInput>
    

    让输入框失去焦点

    <TouchableOpacity
          onPress={()=> {
               this.refs.input.blur(); //让输入框失去焦点
               this.onRightButtonClick();
           } }
    >搜索 </TouchableOpacity>
    
    • 另一种绑定ref方法

    用的时候可以直接使用this.toast调用方法

    <Toast ref={toast=>this.toast = toast}/>
    
    • 指示器ActivityIndicator的使用
           //当加载数据时显示指示器,加载完成后关闭指示器
           let listView=!this.state.isLoading?<ListView
                dataSource={this.state.dataSource}
                renderRow={(e)=>this.renderRow(e)}
            />:null;
            let indicatorView=this.state.isLoading?
                <ActivityIndicator
                    style={styles.centering}
                    size='large'//设置指示器的大小,是枚举值
                    animating={this.state.isLoading}
                />:null;
            let resultView=<View style={{flex:1}}>
                {indicatorView}
                {listView}
            </View>
    
            //样式
            centering:{
                  alignItems:'center',
                  justifyContent:'center',
                  flex:1, //一定要指定flex
             },
    
    • 取消网络请求

    就像在网上买东西一样,卖家已经发货是无法取消订单的,但是我们可以在快递来的时候,选择拒收快递!

    封装取消网络请求类

    export default function makeCancelable(promise) {
        //定义标志位
        let hasCanceled_=false;
        const wrappedPromise=new Promise((resolve,reject)=>{
            promise.then((val)=>{
                hasCanceled_?reject({isCanceled:true}):resolve(val)
            });
            promise.catch((error)=>{
                hasCanceled_?reject({isCanceled:true}):resolve(error)
            })
        });
        //向调用者返回一个字典
        return {
            promise:wrappedPromise,
            //返回cancel方法,并将 hasCanceled_ 置为true
            cancel(){
                hasCanceled_=true;
            }
        }
    
    }
    

    在使用的类里面导入

    import makeCancelable from '../util/Cancleable'
    

    请求数据时调用

    loadData(){
    this.cancelable=makeCancelable(fetch(this.genFetchUrl(this.inputKeky)));
            this.cancelable.promise
                .then(response=>response.json())
                .then(responseData=>{
                    ...
                }).catch(e=>{
                    ...
            })
    

    取消搜索

    //在取消搜索的地方调用
    this.cancelable.cancel();
    
    • 取消网络请求的应用

    当获取网络请求的页面,当数据还未获取到时,用户选择返回上一级界面,这时,网络请求的页面还在请求数据,当获取到数据时,再上一级界面底部会显示警告,这时就需要在组件Will被卸载的方法中 "调用取消网络请求" .

    componentWillUnmount(){
            //this.cancelable  判断this.cancelable是否为空
            this.cancelable&&this.cancelable.cancel();
        }
    
    • React-Native 支持GCD的
    • 在终端中切换到 当前文件某个目录
    比如切换到iOS文件下执行一些操作
    cd iOS /
    
    • 属性的声明 propTypes
    具体参考贾鹏辉自定义 NavigationBar 内容
    static propTypes = {
            name: PropTypes.string,
            ID: PropTypes.string.isRequired, //这个是必须的,不传值会报警告
        }
    
    • webView加载服务器返回的html标签
    <WebView
            ```
           source={{html: this.state.html, baseUrl: ''}}
            ```
    />
    
    • 去除 Android 中输入框的下划线

    那么 Android 中的 TextInput 的下划线是不是丑爆了?这边我们也来处理下它,直接使用 underlineColorAndroid 这个属性,让他为透明即可。

     underlineColorAndroid={'transparent'}
    
    • 获取Realm的路径

    先获取到Documents的路径

      NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
      NSString *docDir = [paths objectAtIndex:0];
      NSLog(@"====Realm的存储路径:%@",docDir);
    

    在Documents的路径下有一个default.realm的文件,该文件就是Realm的文件存储位置,可用Realm Borwser双击打开

    • FlatList滚动到某一行
    this.listView.scrollToIndex({viewPosition: 0, index: 0});
    

    相关文章

      网友评论

          本文标题:RN-笔记

          本文链接:https://www.haomeiwen.com/subject/byachxtx.html