react-native踩过的坑
钩子函数
useRef不可成为依赖项
ref.current 不可以作为其他 hooks(useMemo, useCallback, useEffect)依赖项;ref.current 的值发生变更并不会造成 re-render, Reactjs 并不会跟踪 ref.current 的变化。
useState 异步回调获取不到最新值及解决方案
https://www.cnblogs.com/hymenhan/p/14991789.html
用ref保存再改变状态,
或回调方式获取以前的值,再进行更新状态。
使用 useReducer 仿造类组件中的 forceUpdate 实现组件强制渲染
scrollable-tab-view和tab-view
scrollable-tab-view | tab-view | |
---|---|---|
1 | 没维护了,使用的pager-view也是老版本的 | 一直在维护 |
2 | bug多,如左右滑动时会触发flatlist中的触摸事件 | bug也多,和tab-view同层级的其它组件涉及到高度的动画时,掉帧严重 |
3 | 滑动时,标签没有动画 | 有动画 |
4 | 对ios的兼容不好 | android和ios都兼容 |
react-navigation集成redux
因为在react-navigation v4.x的时候,官网就说了不建议用redux来管理navigation的state,说是会拖慢运行速度,不过仍然提供了redux-helper来集成navigation的state,不过在5.x和6.x中,有关redux的集成,在官网中已经不写了,
也就是说,navigation官网希望navigation自己管理自己的state,redux一边去。
而从redux-helper的github中也可以发现,在2年前提供的最新的代码v4.1中依赖的是5.x的@react-navigation/core,但我在使用后,发现有bug,而redux-helper后期也没继续更新,估计也不想搞了,haha
好吧。
打包失败
扩大使用的堆空间
android - Execution failed for task ':app:minifyReleaseWithR8'老是抛堆内存不够out of memory
在android目录下的gradle.properties增加
#增大使用的堆空间
org.gradle.jvmargs=-Xms1024m -Xmx4096m
uploadCrashlyticsMappingFileXXXRelease 打包connect timed out
https://blog.csdn.net/m0\_37587256/article/details/112028327
Firebase千般好,但是有一样就是他会自动生成mapping之类的文件(android\app\build\crashlytics\release\mappings目录)并打包 .gz 压缩包,自己上传Google,这对国外当然没问题,国内就别想了,
因此关闭自动上传
啥时候手动上传,太简单,发布google play 新版本的时候,自己把生成的 .gz 文件上传就ok了,
buildTypes {
debug {
signingConfig signingConfigs.debug
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
minifyEnabled false
versionNameSuffix "_debug"
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.release //将默认的debug 改成 release
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
shrinkResources true
// Zipalign优化
zipAlignEnabled true
// 设置firebase是否要自动上传
firebaseCrashlytics {
mappingFileUploadEnabled false
}
}
}
打包安装后,打开闪退
第1次发布版本包
使用android studio的logcat(sdk目录下的tools目录中的工具,打开命令窗口,直接输入monitor)
关键字,在过滤那输入java.lang
然后打开app,然后app闪退,在logcat找到报错原因:
java.lang.IllegalStateException: java.lang.NoSuchFieldException: fill
参考:
http://www.uwenku.com/question/p-fhlllpqy-kr.html
https://stackoverflow.com/questions/68643215/how-to-use-minifyenable-in-release-apk
解决:
https://www.codeleading.com/article/27112954688/
我项目里使用了一个 react-native-svg 的库,是它导致的,需要在{project_path}/android/app/proguard-rules.pro 文件里面添加一行:
-keep public class com.horcrux.svg.** {*;}
第2次发布版本包
由于logcat打印的错误不清晰,定位不准确,因此使用了google的firebase中的crashlytics
这次发布包,先后出现了两个错误,
一是no such field exception
是因为这个类被混淆后,另外的类在反射调用的时候找不到字段了,因此不允许该类被混淆,
通过混淆后生成的mapping.txt中找到对应的映射类,然后将其写进proguard-rule.pro中,保留该类,不被混淆
-keep class com.facebook.react.uimanager.** { *; }
第二个异常是com.facebook.jni.CppException
https://github.com/facebook/react-native/issues/26930
通过了解,这个是新js引擎hermes无法运行的异常,将android/app/build.gradle中的IntlJsc注释掉
// 想使用hermes就得注释这个
// def useIntlJsc = false
// if (useIntlJsc) {
// implementation 'org.webkit:android-jsc-intl:+'
// } else {
// implementation 'org.webkit:android-jsc:+'
// }
这个运行,会导致hermes的停止。
并同时保证hermes的相关类不被混淆(不知道起作用没)
-keep class com.facebook.hermes.unicode.** { *; }
启动页
https://www.jianshu.com/p/78571e5435ec
react-native是0.64.0
首先安装yarn add react-native-splash-screen
1.
//android/app/src/main/res/layout/launch_screen.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/launch_screen">
</LinearLayout>
//drawable是res中以该字符串为开头,然后加上”-xhdpi/-xxhdpi”之类的文件夹,
//launch_screen是文件夹中的图片的名字,即想放的启动页的图片名
2.
//android\settings.gradle //通过该设置找到源文件位置
include ':react-native-splash-screen'
project(':react-native-splash-screen').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-splash-screen/android')
//android\app\build.gradle //通过该配置将上述找到的源代码依赖到当前android项目
dependencies {
implementation project(':react-native-splash-screen') # 确认存在, 如果不存在则需要手动增加这行。
}
3.
//MainApplication.java
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();// 会自动添加 new SplashScreenReactPackage()
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
return packages;
}
//MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
SplashScreen.show(this,true); // 添加这一句
}
4.
//最后在js里关掉,一般写在入口页app.js里
import SplashScreen from 'react-native-splash-screen';
componentDidMount(){
SplashScreen.hide();
}
默认props
static defaultProps设置的默认的父组件的props和父组件传进来的props,
constructor() 无法获得默认props.
constructor(props)可以获得props.
PanResponder
react-native中的手势控制组件,坑是在
onStartShouldSetResponderCapture//单点
onStartShouldSetResponder
和
onMoveShouldSetResponderCapture//滑动
onMoveShouldSetPanResponder
不能同时存在,说白了就是,这个手势组件要么控制单点,要么控制滑动,这两个控制事件是互相独立的,官网都没说,在stackOverFlow网站上找到的答案。
https://blog.csdn.net/tianyitianyi1/article/details/107426381这个博客没直说,但是也是这么用的。
gestureState:{
stateID 此次触摸事件的ID
moveX 移动时当前的屏幕坐标
moveY
x0 响应器产生时的屏幕坐标(手势开始时的第一个坐标)
y0
dx 触摸开始累积的横向路程
dy
vx 当前的横向移动速度
vy
numberActiveTouches 触摸点数量
}
没有声明文件t.ds
在src目录下新建一个types目录,然后在types 目录下新建一个 index.d.ts文件然后在文件中添加代码 declare module ‘第三方类库名’
Touchable组件
TouchableWithoutFeedback只接受一个直接子组件
react-navigation无法全屏
如果有需要全屏的组件如视频(video)之类的组件存在于导航页面内(navigation-tab),使用orientation转为横屏后,并将video的宽高设为屏幕高宽后,无法关掉navigation的显示。
此时只能使用react-navigation其中的一个fullScreen的modal,并且只能设置在nativeStackNavigator里,之后使用navigation.navigate进行跳转。
绝对布局
在relative的布局内,如果有多个absolute布局的同级直接子组件,且这些组件中含有Touchable组件时(无论在外层还是在内层),触摸事件只有这些同级子组件中的最后一个能触发。
触摸事件的event
触摸事件处理的回调都有一个 event参数,包含一个触摸事件数据 nativeEvent.
event.nativeEvent:{
changedTouches - 在上一次事件之后,所有发生变化的触摸事件的数组集合(即上一次事件后,所有移动过的触摸点)
identifier - 触摸点的 ID
locationX - 触摸点相对于当前元素的横坐标
locationY - 触摸点相对于当前元素的纵坐标
pageX - 触摸点相对于根元素的横坐标
pageY - 触摸点相对于根元素的纵坐标
target - 触摸点所在的元素 ID
timestamp - 触摸事件的时间戳,可用于移动速度的计算
touches - 当前屏幕上的所有触摸点的集合
}
defaultProps
修改了这个对象,需要reload才能生效。
${}
let name = '彭于晏'
console.log( '名字为:'+name )
console.log( `名字为:${name} ` ) //在 `` 中可以使用 ${} 直接把变量和字符串拼接起来
{}表达式
在jsx里的{}表达式里,若调用无参函数可以直接写上函数名。
但是若要传参数,则必须写成箭头函数,然后在函数体里调用想调的函数。(否则this绑定丢失)。
ref.measure
只有组件带有press事件,只有在press事件的回调方法中可以调用该组件的ref.measure,调用其它组件的ref.measure都是undefined.
获得遍历中每一项的ref
预先定义存储各子项的对象,然后通过索引给每个子项赋值ref.
ref={(ref)=>{
this.downArrowRef[index] = ref;
}}