React Native - 嵌套导航器时,React Navigation 过渡缓慢

18

我正在使用React Native构建一个跨平台的本地应用程序,并使用React Navigation进行屏幕间导航以及使用Redux管理导航状态。 当我嵌套我的导航器时,问题就出现了。

例如,我正在将堆栈导航器用作应用程序的默认导航器。

export const DefaultNavigate = new StackNavigator(
{
        Login: {
            screen: LoginScreen,
        },
        Home: {
            screen: AppDrawerNavigate,
        },
        AppTabNav: {
            screen: AppTabNavigator,
        },
    }
);

我的第一个屏幕是登录屏幕,主屏幕是抽屉式导航。

const AppDrawerNavigate = new DrawerNavigator(
{
        InProcess: {
             screen: InProcess,
        },
        Machine: {
             screen: Machine
        },
        Settings: {
             screen: Settings
        },
        Logout: {
             screen: Logout
        },
        ContactUs: {
             screen: ContactUs
        }
    }
);

当用户点击抽屉导航器中的“机器”时,我将屏幕导航到DefaultNavigator中声明的AppTabNav。

const AppTabNavigator = new TabNavigator(
    {
        MachineList: {
            screen: MachineList,
        },
        CalendarView: {
            screen: CalendarView,
        }
    },
);
作为名称所示,这是一个带有两个屏幕的选项卡导航器,一个使用listview显示列表,另一个使用calendarview显示日历。我的listview数据源中只有大约30-40个项目,因此对它们进行渲染对listview来说是易如反掌的。但是当从任何屏幕通过DrawerNavigator导航到Machine屏幕时,会存在1-2秒的延迟以及js线程下降到-2.1,这真的减缓了过渡速度。每当我在抽屉导航器中嵌套选项卡导航器时,都会出现这种下降 如果有人需要在抽屉导航器中使用Machine屏幕的代码,这里就是它:
componentDidMount() {
    if(this.state.loaded)
        this.props.navigation.dispatch({ type: MACHINE});
}
render() {
    return <AppActivityIndicator />
}
以下是我处理屏幕导航的 reducer 代码:
case types.MACHINE:
  nextState = DefaultNavigate.router.getStateForAction(
    NavigationActions.reset({
      index: 1,
      actions: [
        NavigationActions.navigate({ routeName: 'Home' }),
        NavigationActions.navigate({ routeName: 'AppTabNav' })
      ]
    }),
    state
  );

以下是抽屉导航器中MachineList屏幕的渲染方法:

render() {
    return (
        <View style={styles.container}>
            <AppStatusBar />
            <ListView
                initialListSize={10}
                dataSource={this.state.dataSource}
                renderRow={this.renderRow.bind(this)}
                enableEmptySections={true}
            />
        </View>
    );
}

请帮我解决这个问题。我做错了什么?

依赖关系

"dependencies": {
    "native-base": "^2.3.1",
    "react": "16.0.0-alpha.12",
    "react-devtools": "^2.5.0",
    "react-native": "0.47.1",
    "react-native-calendars": "^1.5.8",
    "react-native-vector-icons": "^4.3.0",
    "react-navigation": "^1.0.0-beta.11",
    "react-redux": "^5.0.6",
    "redux": "^3.7.2",
    "redux-logger": "^3.0.6",
    "redux-persist": "^4.9.1",
    "redux-thunk": "^2.2.0"
},
"devDependencies": {
    "babel-jest": "20.0.3",
    "babel-preset-react-native": "3.0.0",
    "jest": "20.0.4",
    "react-test-renderer": "16.0.0-alpha.12"
},

你找到解决方案了吗? - Sara Inés Calderón
没有找到解决方法,但是我使用了一个变通方法。我在componentWillMount中使用timeout函数将组件的渲染延迟了10毫秒。虽然JS线程会出现下降,但UI不会受到影响。也许这是糟糕的编码或React导航嵌套问题。 - Shivansh Singh
9个回答

22

我遇到了同样的问题。在切换屏幕时存在显著的延迟。我发现这篇博客非常有用:https://novemberfive.co/blog/react-performance-navigation-animations/

所以问题是

当一个新的屏幕被推送时,React Navigation 会在屏幕上先渲染它,然后再将其动画化到位。这意味着当一个复杂的屏幕有许多组件,需要几百毫秒才能轻松渲染时,会出现问题。

为了解决这个问题,我使用了InteractionManager。它基本上会在所有动画完成后给你回调。

以下是我所做的,以避免延迟,并在修复后使应用程序正常工作。希望这可以帮助。

// @flow

import React, { Component } from 'react';
import { InteractionManager, ActivityIndicator} from 'react-native';

class Team extends Component<Props> {
 state = {
    isReady : false
 }
 componentDidMount() {
   // 1: Component is mounted off-screen
   InteractionManager.runAfterInteractions(() => {
     // 2: Component is done animating
     // 3: Start fetching the team / or render the view
    // this.props.dispatchTeamFetchStart();
     this.setState({
       isReady: true
     })
   });
 }

 // Render
 render() {
  if(!this.state.isReady){
  return <ActivityIndicator />
  }
  return(
   //  Render the complex views
   )
   ...
 }
}

export default Team;

1
我无法证实您的答案,但我认为这也会起作用,就像我说的那样,在推送屏幕之前延迟10ms可以解决延迟问题,所以它也在做同样的事情。感谢您的回答。 - Shivansh Singh
了不起的解决方案 - TheEhsanSarshar
1
嘿@Carlos,我应该在每个屏幕中使用InteractionManager.runAfterInteractions(()=>{})吗?我遇到了它的缓慢转换问题。 - Oliver D
1
这个答案提到的博客已经不存在了。以下是它的wayback machine链接:https://web.archive.org/web/20201127164304/https://www.novemberfive.co/blog/react-performance-navigation-animations - BruceHill
这是博客文章的新链接:https://medium.com/@NovemberFive/fixing-common-performance-problems-in-react-navigation-8eea6dc7c5ba - undefined
显示剩余2条评论

6

我在应用程序中遇到了相同的问题。与其他人描述的类似,我有嵌套的Stack和Drawer导航器。对于我来说,迟滞是在嵌套的Stack Navigator之间的屏幕切换时出现的。

我尝试使用InteractionManager解决这个问题,但似乎没有什么区别。最终,我发现在延迟渲染大组件方面建立一个简单的超时会有很大的改进。

因此,我编写了这个简单的useIsReady钩子,现在我在我的屏幕上使用它:

import { useEffect, useState } from "react";

const useIsReady = () => {
  const [isReady, setIsReady] = useState(false);

  useEffect(() => {
    setTimeout(() => setIsReady(true), 100);
  }, []);

  return isReady;
};

export default useIsReady;

以下是我在屏幕中使用Hook的方式:

import React, { memo } from "react";
import { useTheme } from "react-native-paper";
import { View, ActivityIndicator } from "react-native";
import { useIsReady } from "hooks";

const BusyIndicator = () => {
  const theme = useTheme();
  return (
    <View style={{ flex: 1, justifyContent: "center" }}>
      <ActivityIndicator size="large" color={theme.colors.primary} />
    </View>
  );
};

const Camera = () => {
  const isReady = useIsReady();

  if (!isReady ) {
    return <BusyIndicator />;
  }
  
  return (
    <> ... </>
  );
};

export default memo(Camera);

我发现这样做有很大的改善作用,我的屏幕转换现在完全顺畅。


1
喜欢这个方法的简单而强大! - Armster

2

我遇到了同样的问题,对我来说使用NativeStackNavigator而不是StackNavigator有所帮助。


1
我曾经遇到过同样的问题。以下步骤帮助我大大减少了延迟时间:
  1. 如果您在定义屏幕时使用redux的compose包装组件(特别是BottomTabBar中的组件),最好将其删除。这将大大提高过渡动画的平滑度和速度。

  2. 正如@Carlos所强调的那样,使用InteractionManager.runAfterInteractions(()=>{})


1
将回调函数传递给预定义的requestAnimationFrame方法(无需导入),它会在动画完成后自动调用该函数。
requestAnimationFrame(() => {
   navigation.navigate('routeName') //enter code here
}

1

优化内存使用和性能 (v5)

对于那些使用React Navigation版本5的人

首先,您需要按照 react-native-screens的安装说明进行安装。之后,在导航堆栈呈现之前(通常在index.js或App.js文件中),使用以下代码片段:

// Before rendering any navigation stack

import { enableScreens } from 'react-native-screens';
enableScreens();

0

我在使用堆栈导航器时也遇到了这个问题,抽屉式导航器的效果更好。如果堆栈中有超过100行带有某些导入组件的内容,它会开始延迟和延迟导航。

我在堆栈屏幕中使用InteractionManager,在动画后呈现组件。

它起作用了,但是当我点击返回时,动画就会出现颤动/延迟。

我查看了https://reactnavigation.org/docs/react-native-screens/

因为React-Navigation已经使用本地导航组件,所以它说:

要禁用并使用RN视图:-

import {enableScreens} from 'react-native-screens'
enableScreens(false) 

我禁用了它,现在它的性能比原生的好多了,不知道为什么。目前我处于调试模式,所以不能确定是远程视图的原因...

而且真的很困惑为什么在原生导航时会出现卡顿,而在 RN 视图中却没有。有人可以告诉我为什么吗?我想使用原生导航。


它对我有效,你尝试过interactionManager了吗? - Vivek sharma

0

import { useCallback, useState } from 'react'
import { InteractionManager } from 'react-native'
import { useFocusEffect } from 'expo-router'

const useIsReady = () => {
  const [isReady, setIsReady] = useState(false)

  useFocusEffect(
    useCallback(() => {
      InteractionManager.runAfterInteractions(() => {
        setTimeout(() => setIsReady(true), 100)
      })

      return () => {
        setIsReady(false)
      }
    }, [])
  )

  return isReady
} 

export default useIsReady

这是对@BruceHill在expo回答的修改版本。

0

我遇到了缓慢的选项卡导航问题,当像我这样有嵌套的事物时,就会出现缓慢的问题

  • 我有来自react-navigation/drawer的抽屉 第一级

  • 其次,我在抽屉下面有来自'@react-navigation/bottom-tabs'的选项卡 第二级

第三件事是我有一个堆栈(它有多个屏幕)从@react-navigation/stack在选项卡下面 第三级

<Drawer.Navigator>
  <Tab.Navigator>
    <Stack.Navigator>
      <Stack.Screen name="Home"></Stack.Screen>
      <Stack.Screen name="Details"></Stack.Screen>
    </Stack.Navigator>
     <Stack.Navigator>
       <Stack.Screen name="Task"></Stack.Screen>
       <Stack.Screen name="Details"></Stack.Screen>
     </Stack.Navigator>
     .....
  </Tab.Navigator>
</Drawer.Navigator>

所以,如果我从上面删除任何一个东西,慢速问题就会消失,例如,如果我删除Drawer.Navigator,那么我只有两个嵌套的东西Tab.Navigator和Stack.Navigator,问题就解决了。

所以我做的是删除抽屉并使用来自原生基础的抽屉(您可以使用另一个抽屉包)。

   <Tab.Navigator>
       <Stack.Navigator>
           <Stack.Screen name="Home"></Stack.Screen>
           <Stack.Screen name="Details"></Stack.Screen>
       </Stack.Navigator>
      <Stack.Navigator>
           <Stack.Screen name="Task"></Stack.Screen>
           <Stack.Screen name="Details"></Stack.Screen>
       </Stack.Navigator>
       .....
   </Tab.Navigator>

我知道这种痛苦,我已经遭受了很长时间,并且没有从官方的react-navigation团队中找到任何解决方案。希望团队能够尽快解决嵌套问题,但现在你可以使用这个解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接