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

Shi*_*ngh 3 react-native redux react-navigation

我正在构建一个使用react-native的跨平台本机应用程序,并使用react-navigation在屏幕之间来回导航以及使用redux管理导航状态。当我嵌套导航器时,就会出现问题。

例如,我使用Stack Navigator作为应用程序的默认导航器。

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

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

const AppDrawerNavigate = new DrawerNavigator(
{
        InProcess: {
             screen: InProcess,
        },
        Machine: {
             screen: Machine
        },
        Settings: {
             screen: Settings
        },
        Logout: {
             screen: Logout
        },
        ContactUs: {
             screen: ContactUs
        }
    }
);
Run Code Online (Sandbox Code Playgroud)

当用户在Drawer Navigator中单击Machine时,我正在将屏幕导航到DefaultNavigator中声明的AppTabNav。

const AppTabNavigator = new TabNavigator(
    {
        MachineList: {
            screen: MachineList,
        },
        CalendarView: {
            screen: CalendarView,
        }
    },
);
Run Code Online (Sandbox Code Playgroud)

这是一个带有两个屏幕的标签导航器,顾名思义,一个屏幕使用列表视图显示列表,另一个屏幕使用日历视图显示日历。我的listview的dataSource中只有大约30-40项,因此渲染它们对于listview来说是小菜一碟。但是,当从DrawerNavigator从任何屏幕导航到Machine屏幕时,会有1-2秒的延迟,并且js线程下降到-2.1,这实际上在减慢转换速度。每当我将选项卡导航器嵌套在抽屉式导航器中时,这种下降都很常见

如果有人在抽屉导航器中需要“机器”屏幕的代码,

componentDidMount() {
    if(this.state.loaded)
        this.props.navigation.dispatch({ type: MACHINE});
}
render() {
    return <AppActivityIndicator />
}
Run Code Online (Sandbox Code Playgroud)

以下是我的减速器代码,用于处理屏幕导航,

case types.MACHINE:
  nextState = DefaultNavigate.router.getStateForAction(
    NavigationActions.reset({
      index: 1,
      actions: [
        NavigationActions.navigate({ routeName: 'Home' }),
        NavigationActions.navigate({ routeName: 'AppTabNav' })
      ]
    }),
    state
  );
Run Code Online (Sandbox Code Playgroud)

以下是抽屉导航器中MachineList屏幕的呈现方法,

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

请帮助我。我究竟做错了什么?

相依性

"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"
},
Run Code Online (Sandbox Code Playgroud)

Car*_*los 6

I was facing the same issue. There was a substantial delay while switching the screen. I found this very useful blog https://novemberfive.co/blog/react-performance-navigation-animations/

So the problem was

When a new screen is pushed, React Navigation will initially render it off-screen and animate it into place afterward. This means that when a complex screen with lots of components that easily takes a few hundred milliseconds to render is pushed

To fix this, I used InteractionManager. It basically gives you the callback once all the animation has been completed.

Following is what I have done to avoid delay and app was working fine after the fix. Hope this helps.

// @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;
Run Code Online (Sandbox Code Playgroud)


Bru*_*ill 6

我的应用程序遇到了同样的问题。与其他人所描述的类似,我嵌套了堆栈和抽屉导航器。对我来说,延迟是嵌套堆栈导航器中屏幕之间的转换。

我尝试使用InteractionManager来解决这个问题,但似乎没有太大区别。最终我发现构建一个简单的超时来延迟大型组件的渲染会产生巨大的差异。

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

import { useEffect, useState } from "react";

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

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

  return isReady;
};

export default useIsReady;
Run Code Online (Sandbox Code Playgroud)

这就是我在屏幕中使用钩子的方式:

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);
Run Code Online (Sandbox Code Playgroud)

我发现这让世界变得不同,我的屏幕转换现在完全流畅。