react-native 2022 踩坑记录

1,134 阅读7分钟

1、ios 不支持汉字斜体问题

react-native fontStyle:italic 只针对英文生效

解决方案:

//为Text 添加2d变换实现倾斜
{transform: [{ skewX: '-15deg' }],}

2、针对ui设计的大小建议只做参考,大多数时候,他们会要求不区分屏幕尺寸呈现相同的效果。

解决方案: 设计按钮大小排布间距的时候使用

const {width} = Dimensions.get('window')

使用这个宽度 - 间隔 ➗ 排布数量是最合适的解决方案(注:少走弯路~)

虽然在pad或类似超大尺寸设备上会和难看~

3、子组件通信 子-》父-》子 太过冗杂

解决方案: NativeEventEmitter原生事件很好用。 react-native 提供的原生事件。并不推荐所有的数据传递或者事件监听触发都使用这个,这会让代码看起来一团糟。

但是同一页面的兄弟通信切不涉及数据传递,还是推荐使用

import {NativeEventEmitter} form 'react-native'

//child1.tsx
NativeEventEmitter.emit(xxx,{})

//child2.tsx
NativeEventEmitter.addListener(xxx,()=>any)

4、弹窗冲突问题

解决方案: 统一塞到队列里,先进先出。有排序需求就先排序再出

5、在textInput 限制最大长度的情况下,复制一段长文本的值进去长文本会被切割,且无法得到具体的值

解决方案:查了一下,没有找到解决方案,只有放开最大长度的限制。并通过js限制

6、两个 textInput 的onfocus 切换,不需要调用onblur()事件,调用后会造成页面的闪动现象。

解决方案:

//直接

ref.current.onFocus()

7、 在Text 显示不会空格

解决方案:添加\xa0\xa0\xa0\xa0\xa0\xa0

实测➕6个大概是空两格的效果

8、替换git 登录信息错误

使用mac 安装完xcode,一般xcode会写入git配置

credential.helper=osxkeychain

解决方案: 验证信息会默认存在钥匙链上,重新登陆git需要在钥匙链上删除对应git缓存

9、Dimensions 的宽度测量不正确,从横屏页面跳转过来

解决方案:如果需要在组件内部实时获取到当前window的width。需要使用正确的方法

function A (){
    // ok
      const {width} = useWindowDimensions()
      
      //error
      const {width} = Dimension.get('window')

}

10、图片在实时设置了宽高时,在宽高变化时,图片未正常渲染,呈现了被截取一部分的状态

解决方案:需要在修改 属性 resizeMode: contain

11、调用navigation.goBack()的行为和左上角按钮的返回的行为并不是一致的,需要注意

调用navigation.goBack()。beforremove 监听到的事件类型是'GP_BACK',而右上角点击后触发的事件类型为‘POP'。所以处理返回监听的时候需要记住这一点

12、在ios手机更新到最新的系统后,使用xcode打包提升不支持设备

解决方案:更新xcode到最新版,但是有时候因为mac系统没更新,所以没有办法更新xcode。可以使用以下方法 github.com/filsv/iPhon…

下载需要的ios版本支持,将下载的支持文件放入以下目录,可以启用支持

open /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/

13、安卓打包失败提示compileSdkVersion 错误,或者其他编译错误

检查 android/build.gradle 配置

  buildToolsVersion = "30.0.2"
        minSdkVersion = 21
        compileSdkVersion = 30
        targetSdkVersion = 30
        ndkVersion = "21.4.7075529"

打开android studio 检查编译选项的配置是否满足配置寻求

14、禁止安卓暗黑模式

解决方案:

//android/app/src/main/res/values/styles.xml

  <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="android:forceDarkAllowed">false</item>
        <item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
    </style>
    

注意一定要添加android:forceDarkAllowed false

15、禁用手势未正确关闭。

如果发现手势被禁用,那么大概率是关闭手势事件忘了移除,或者错误移除。不是什么玄幻事件

16、引入字体自定义字体时,ios显示字体android不显示 或者出现相反情况。

解决方案: android 和ios引入字体名称时引入规则不同。android 自己引入字体命名就可以,ios需要引入字体打开后显示的名字。所以需要区别应用

    fontFamily:
      Platform.OS === 'android'
        ? 'Roadgeek 2005 Engschrift Regular'
        : 'Roadgeek 2005 Engschrift',

17、TextInput placeholder熟悉,需要实现占位符的换行

解决方案:

 placeholder={`1、上午十点和同事开会,讨论进去规划;${'\n'}2、中午和朋友去吃创意菜,要记得带给他的礼物。${'\n'}3、下午三点要去提交材料;${`\n`}4、晚上约了宠物医院,要带小猫去体检,记得带上疫苗本`}

只有这样才能生效

18、ahooks useInterval 返回值clear()回调崩溃

解决方案: 需要将ahooks版本升级到3.0,老版本的useInterval没有clear ,然而浏览ahook的文档一般都是最新的文档,需要注意这个差异

yarn  upgrade ahook

19、 安装lottie-ios@3.4.0 依赖时报错报错

执行pod intsall时会报错,错误信息为

image.png

cocospod不能找到可兼容的lottie-ios的版本

这是插件的问题,目前作者还没解决。可能下个版本还未修复。

解决办法有两个

1、降低lottie-ios的版本为3.2.3 问题暂时得到解决

2、 修改node_modules/lottie-react-native/lottie-react-native.podspec

   //del
    s.dependency 'lottie-ios', '~> 3.2.3'
   //add
    s.dependency 'lottie-ios', '~> 3.4.0'
    

以上两个办法能解决这个问题,第二个方法请配个patch-package 使用

20、动画无法播放

ui 基于AE lottie导出的插件可能是无法使用,或者兼容性存在问题的。检查方法为将导出的json文件上传到

lottiefiles.com/

这个网站上测试一下,看下是否正常、避免反复折腾。

21、测量静态图片大小

解决方案:Image 提供了一个静态方法可以测量图片的原始值,可以利用这一点来获取原始宽高

      Image.resolveAssetSource(source);

22、弹窗的输入框被弹出的键盘挡住

这种情况keyboardAvoidScrollView 解决不了,最后决定手动加个动画,去移动弹窗的位置,当输入OnFocus时,触发动画,抬升弹窗、onBlur 时移除动画,复原位置

import Animated, {
  Easing,
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated';

 const animatedStyles = useAnimatedStyle(() => ({
    top: withTiming(offset.value, {
      duration: 500,
      easing: Easing.bezier(0.25, 0.1, 0.25, 1),
    }),
  }));
    const offset = useSharedValue(0);

//template
  <TextInput

              onFocus={() => {
                offset.value = Platform.OS === 'ios' ? -100 : 0;
              }}
              onBlur={() => {
                offset.value = 0;
              }}
      
            />

23、重复高速点击按钮的防抖和节流整么处理

长期困扰开发的一个问题,使用正常的防抖节流处理避免不了快速点击出现的问题,在触发动作前还是会有一些点击空间。会触发一些恶性bug导致崩溃

解决方案: 经过一翻探讨、最后决定使用双重保险来解决这个问题,任何涉及到跳转或者访问借口的方法都需要添加两个东西 useLockFn 和主动 sleep

useLockFn 来自ahook,在 submit 函数执行完成前,其余的点击动作都会被忽略。

sleep 为中断线程

useLockFn(()=>{
    xxx
    
    sleep()
})

在两者的共同作用下,彻底解决了防抖

24、在页面focus时需要调用函数,在页面unFocus时也需要调用函数

在开发过程中遇到需要有这种需求的地方越来越多。于是我们新增了一个通用的Hook useFocusChangeEffect

function useFocusChangeEffect(
  focusCallback: () => void,
  depend: any[],
  blurCallback?: () => void,
) {
  const isFocused = useIsFocused();
  useEffect(() => {
    let callback;
    if (isFocused) {
      callback = focusCallback();
    } else if (blurCallback) {
      blurCallback();
    }
    if (typeof callback === 'function') {
      return callback;
    }
    return () => {};
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isFocused, ...depend]);
}

使用方法和useEffect 一致,区别是第一个参数函数会在onfocus 的调用,第三个参数函数会在onBlur时调用。此hook成为使用最高的hook

25、返回时依据来时路由跳转到目标位置

返回路由时需要根据实际历史路由判断返回到哪里,当有这种需求时,就需要获取历史记录 核心时监听路由变化,当返回时获取routes = navigation.getState(),然后通过记录判断

import {
  NavigationProp,
  RouteProp,
  StackActions,
  useNavigation,
  useRoute,
} from '@react-navigation/native';

  const backFunc = useCallback(
    (e: any) => {
      e.preventDefault();
      if (e.data.action.type === 'GO_BACK') {
        const { routes } = navigation.getState();
        navigation.removeListener('beforeRemove', backFunc);
        if (
          routes[routes.length - 2].name === 'xxx'
          const popAction = StackActions.pop(3);
          navigation.dispatch(popAction);
        } else navigation.dispatch(e.data.action);
      } else {
        navigation.dispatch(e.data.action);
      }
    },
    [navigation],
  );
  useFocusChangeEffect(
    () => {
      navigation.addListener('beforeRemove', backFunc);
      return () => navigation.removeListener('beforeRemove', backFunc);
    },
    [],
    () => {
      navigation.removeListener('beforeRemove', backFunc);
    },
  );

26、转化屏幕显示方向的同时退出当前页面,会引起页面卡死在转化的页面

解决方案,在调用navigation.goBack()时,需等待方向转化执行完毕,即需要手动加一点延迟。

   Orientation.lockToPortrait();
    setTimeout(() => {
      navigation.goBack();
    }, 400);

这样就不会引起卡死

27、静态页面添加微信的适配

微信浏览器无法直接下载安装包,需要用户使用其他浏览器打开,所以需要这么一个提示。

      var isWx = u.toLowerCase().match(/MicroMessenger/i) == "micromessenger";
      var isWxWork = u.toLowerCase().match(/WxWork/i) == "wxwork";
      if (isWx || isWxWork) {
        modal.style.display = "block";
      }
      
      <div  class="modal"  style="display:none">
				<div class="content">
				  <img src="./img/arrow.png" class="arrowImg" />
				  <div class="hint">
					点击右上角<img src="./img/more.png" class="moreImg" alt="" />打开菜单
				  </div>
				  <div class="hint">
					选择<img
					  src="./img/browser.png"
					  class="browserImg"
					  alt=""
					/>用浏览器打开下载
				  </div>
				</div>
			  </div>

28、图表的纵轴标识为图片的情况

因为chart 需要跑在WebView中,所以想要纵轴是图片就需要将图片转化成二进制形式,放入图表config中。

解决方案:是将原本值转化为匹配规则,通过富文本自定义显示,为匹配到的规则添加背景图,以此来完成图片的显示

  yAxis: {
    type: 'category',
    data: ['4', '3', '5', '6', '1', '7', '0', '2'],

    axisLine: {
      show: false,
    },
    splitLine: {
      show: true,
    },
    axisTick: {
      show: false,
    },
    axisLabel: {
      formatter: (value: string) => {
        'show source';

        const rule: Record<string, string> = {
          '0': 'happy',
          '1': 'calm',
          '2': 'rod',
          '3': 'angry',
          '4': 'cry',
          '5': 'worry',
          '6': 'speechless',
          '7': 'leisure',
        };
        return `{${rule[value]}|}`;
      },
      margin: 20,
      rich: {
        happy: {
          fontSize: 24,
          backgroundColor: {
          
            image: happy,
          },
        },
        calm: {
          fontSize: 24,
          align: 'center',
          backgroundColor: {
            image: calm,
          },
        },
        rod: {
          fontSize: 24,
          align: 'center',
          backgroundColor: {
            image: rod,
          },
        },
        angry: {
          fontSize: 24,
          align: 'center',
          backgroundColor: {
            image: angry,
          },
        },
        cry: {
          fontSize: 24,
          align: 'center',
          backgroundColor: {
            image: cry,
          },
        },
        worry: {
          fontSize: 24,
          align: 'center',
          backgroundColor: {
            image: worry,
          },
        },
        speechless: {
          fontSize: 24,
          align: 'center',
          backgroundColor: {
            image: speechless,
          },
        },
        leisure: {
          fontSize: 24,
          align: 'center',
          backgroundColor: {
            image: leisure,
          },
        },
      },
    },
  },

29、手机号匹配限制的太死导致上线出现问题

手机号登陆时的限制应该放松一点,只需要校验9个数字就好

(!/^1[123456789]\d{9}$/.test(phone))

30、TextInput ios和安卓存在差异化

TextInput 在不同平台上的样式不相同,主要体现在padding上。所以使用TextInput组件时,先将padding设置为0。抹平差异

31、安卓打包时出现OutOfMemoryError

如果打包的文件很多,会出现OutOfMemoryError,导致问题出现的原因是react-native后台进程默认分配的内存不够 安卓打包依赖于jvm,查看/android/gradle.properties 文件

# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m

org.gradle.jvmargs=-Xmx1024m -XX:MaxPermSize=256m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
将jvm参数修改一下就能解决问题,成功打包
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

32、使用安卓真机开启debug调试时,有时会出现不能跳页,点击不动的情况

这种情况如果启用toggle inspector,查看点击元素就会发现实际上是没有点击事件。导致问题的原因是因为安卓手机时间与mac时间不一致。调用adb命令,可以看到如果误差在一秒以上,就会出现此问题。 解决问题的方法就是是时间一致。我的解决方案为关闭mac和安卓的同步网络时间。并手动更改时间。就能解决问题

33、两个modal 同时渲染会出现页面卡死,无法点击的问题

这个问题非常常见,其涵盖的范围为react-native的modal组件和Toast,很多bug都是这问题导致的。其会导致以下问题

  • 不能在modal里面弹出modal,会卡死
  • 不能同时打开两个modal
  • 关闭前一个modal时打开下一个需要使用setTimeout()做异步,来避免上一个modal未完全关闭就打开下一个
  • 其他类似问题 解决方案,为避免同时出现两个modal,或者用View来做弹窗最好的自己写个传送门

34、ScrollView 不能嵌套使用flatList

ScrollView 中不能使用flatList,会报错。虽然ScrollView 加flatList 的应用场景非常多,社区反馈也很多。但是官方还是没有什么解决方案,想要在flatList 中插入想要滚动的内容只能使用flatList 提供的headerRender和footerRender。 但是这样会出现问题,一旦list 更新,你的内容就会一起刷新

35、react-native-swiper建议不要使用

。react-native-swiper 有9.8k star,但是社区不活跃,很多issue未解决。最后更新时间也在2020年。 而且问题也很多: 1、初始化的时候必须有值,如果没有值的话就会出很多奇奇怪怪的bug。但是这个必须有值也是社区issue提出的 2、重复刷新的时候会计算错误 建议使用react-native-EzSwiper

36、textInput存在在modal上 autoFocus 不能弹出

需要使用ref+ setTimeout() textInput 的autoFocus属性会弹出软键盘。但是如果textInput在modal上。autoFocus 属性就会失效,软键盘不会弹出。解决方案为在使用ref,异步设置focus

37、“parserOptions.project“ has been set for @typescript-eslint/parser

这个问题查阅type-eslint官方的资料以及issue。发现是一个常驻的问题。且官方不会解决。导致的问题的原因是同意个目录下存在两个名字相同的文件。 但是问题是目录下没有两个一样的文件也会出现这个问题。type-eslint 的解析器需要设置tsconfig。tsconfig 需要指明include那些文件。如果没有包含就会报错。