阅读 128

☀️学习系列:慕课网新版rn遇到的问题

在开发过程中遇到的问题总结

一、 Unable to resolve module react-native-gesture-handler from node_modules\@react-navigation\native\lib\module\Scrollables.js: react-native-gesture-handler could not be found within the project.

执行以下2个命令安装并link

npm install --save react-native-gesture-handler

react-native link react-native-gesture-handler(16+版本不需要)

二、createStackNavigator() has been move to react-navigation-stack

由于React Navigation 更新,createBottomTabNavigator,createStackNavigator无法继续在react-navigation直接引入,而是需要在新的包react-navigation-tabs和react-navigation-stack里引入,基本语法暂未发现变化;

npm install --save react-native-stack

三、can not resolve module "react-native-screens"

npm install --save react-native-screens

四、Execution failed for task ':app:processDebugResources'.

进入android目录执行

./gradlew clean

五、The navigation props is missing for you must setup you app

这是因为在navigation4+当中,所有对外的组件都需要用createAppContainer包裹,如下

render() {
        const Tab = createAppContainer(this._tabNavigator());
        return <Tab />;
}
复制代码

六、react-native-vector-icons图标不正常显示

安卓平台下:在android/app/build.grade文件的最后加上并重启即可

project.ext.vectoricons = [
  iconFontNames: [ 'MaterialIcons.ttf', 'EvilIcons.ttf' ] // 你想要复制的字体文件的名字
]

apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
复制代码

七、在rn最热页面跳转到详情页中 onPress随着render自动执行

原因:在RN的页面代码中,花括号 {} 有取对象值的意思在內,而方法也是对象的一种,因此,这里并不单单是为onPress指定属性值那么简单,在赋值的同时还自动调用了一次该响应方法。 使用箭头函数为onPress定义响应函数,箭头函数內调用对应的响应函数即可。

onPress={()=>{
}}
复制代码

八、禁用黄色警告

console.disableYellowBox=true;
复制代码

九、dispatch导致重新render

在集成redux中通过Button按钮点击触发dispatch改变state实现改变主题色的效果,但是由于state改变 注入state的组件重新render了 导致会产生还原的一个效果这时:可以将组件保存在变量中 下次render的时候判断是否生成过,生成过直接返回

    
    _tabNavigator() {
        if(this.Tabs){
            return this.Tabs;
        }
        const {PopularPage,MyPage,TrendingPage,FavouritePage}=TABS;
        const tabs={PopularPage,MyPage,TrendingPage,FavouritePage};
        return this.Tabs=createAppContainer(createBottomTabNavigator(tabs,{
            tabBarComponent:props=>{
                return <TabBarComponent 
                    theme={this.props.theme}
                    {...props}
                />
            }
        }))
    }

    render() {
        
        const Tab = createAppContainer(this._tabNavigator());
        return <Tab />;
    }
复制代码

十、自定义redux中间件检测action的变化(实用)

//action是项目中触发的action
//store.getState()可以实时查看路由的变化
const logger=store=>next=>action=>{
   if(typeof action==="function"){
       console.log("dispatch is a function")
   }else{
       console.log("dispatching",action);
       const result=next(action)
       console.log("nextState",store.getState())
   }
};
复制代码

十一、处理redux中物理返回键返回上一页问题

在rn中 返回键会返回到主菜单中 所以需要一些操作

import {BackHandler} from 'react-native';
import {NavigationActions} from 'react-navigation';

componentDidMount(){
       BackHandler.addEventListener("handwareBackPress",this.onBackPress);//物理返回键的监听
   }

componentWillUnmount(){
   BackHandler.removeEventListener("handwareBackPress",this.onBackPress);
}
/**
    * 处理Android中的物理返回键
    */
   onBackPress=()=>{
       const {dispatch,nav}=this.props;
       if(nav.routes[1].index===0){
           //nav.routes[1]说明是RootNavigator中的MainNavigation如果下标是0不处理
           //为0表示路由导航页面已经没有页面可以退了
           return false;
       }
       //返回
       dispatch(NavigationActions.back());
       return true;
   }

复制代码

十二、离线缓存框架的搭建

即为了更好的用户体验 用户在手机无网络时也可以查看到数据

import { AsyncStorage } from 'react-native';

export default class DataStore{
   /**
    * 获取数据 优先获取本地数据 如果本地数据过期获取远程数据
    * @param {*} url 
    * @param {*} data 
    * @param {*} callback 
    */
   fetchData(url){
       return new Promise((resolve,reject)=>{
           this.fetchLocalData(url).then(wrapData=>{
               if(wrapData&&DataStore.checkTimestampValid(wrapData.timestamp)){
                   resolve(wrapData)
               }else{
                   console.log(DataStore.checkTimestampValid(wrapData.timestamp))
                   this.fetchNetData(url).then(data=>{
                       resolve(this._warpData(data));
                   }).catch((error)=>{
                       reject(error);
                   })
               }
           }).catch(error=>{
               this.fetchNetData(url).then(data=>{
                   resolve(this._warpData(data));
               }).catch(error=>{
                   reject(error);
               })
           })
       })
   }

   //保存数据
   saveData(url,data,callback){
       if(!data ||!url) return;
       AsyncStorage.setItem(url,JSON.stringify(this._warpData(data),callback))
   }

   _warpData(data){
       return {data:data,timestamp:new Date().getTime()}
   }
   /**
    * 获取本地数据
    */
   fetchLocalData(url){
       return new Promise((resolve,reject)=>{
           AsyncStorage.getItem(url,(error,result)=>{
               if(!error){
                   try{
                       resolve(JSON.parse(result));
                   }catch(e){
                       reject(e);
                       console.log(e);
                   }
               }else{
                   reject(error);
                   console.error(error);
               }
           })
           
       })
   }

   /**
    * 网络获取数据
    * @param {}} timestamp 
    */
   fetchNetData(url){
       return new Promise((resolve,reject)=>{
           fetch(url)
           .then(response=>{
               if(response.ok){
                   return response.json();
               }
               throw new Error("network response was not ok")
           })
           .then(responseData=>{
               this.saveData(url,responseData)
               resolve(responseData)
           })
           .catch(error=>{
               reject(error);
           })

       })
   }
   
   /**
    * 检查timestamp是否在有效期内
    */
   static checkTimestampValid(timestamp){
       const currentDate=new Date();
       const targetDate=new Date();
       targetDate.setTime(timestamp);
       if(currentDate.getMonth()!==targetDate.getMonth()) return false;
       if(currentDate.getDate()!==targetDate.getDate()) return false;
       if(currentDate.getHours()-targetDate.getHours()>4) return false;
       return true;

   }
}
复制代码

其中fetchData函数是框架的核心函数,意思是请求数据,先请求本地缓存数据 看是否有数据并且检查过期时间 如果超过过期时间 则重新请求。

十三、如何动态设置redux的key

在切换navbar时 需要请求相应的Java Android模块下的数据 如何动态设置Key值和对应的value值。

switch (action.type) {
        case Types.LOADING_POPULAR_SUCCESS:
            return {
                ...state,
                [action.storeName]: {
                    ...[action.storeName],
                    item: action.items,
                    isLoading: false
                },
            }
        case Types.POPULAR_REFRESH:
            return {
                ...state,
                [action.storeName]: {
                    ...[action.storeName],
                    isLoading: true
                },
            }
        case Types.LOAD_POPULAR_FAIL:
            return {
                ...state,
                [action.storeName]: {
                    ...[action.storeName],
                    isLoading: false
                },
            }
        default:
            return state;
    }
复制代码

十四、ViewPropsTypes报错

暂时没找到解决方案 采用下图方式代替

由于不渲染问题造成的问题

可以借助rn的一个重要api(DeviceEventEmitter)

import { DeviceEventEmitter  } from 'react-native';

function click(){//在事件触发时分发事件
    DeviceEventEmitter.emit(EVENT_TYPE_TIME_SPAN_CHANGE,tab);
}
componentDidMount(){
        this.timeSpanChangeListener=DeviceEventEmitter.addListener(EVENT_TYPE_TIME_SPAN_CHANGE,(timeSpan)=>{
            console.log("change time")
             
        })
}
componentWillUnmount(){
        if(this.timeSpanChangeListener){
            this.timeSpanChangeListener.removeListener();
        }
}
复制代码