ReactNative ☞ react-navigation 3.x

React Native 中文网
React Navigation 官网

简介

一、安装步骤
1、打开终端,cd到工程所在文件夹

2、yarn add react-navigation

3、yarn add react-native-gesture-handler

4、react-native link react-native-gesture-handler
二、关于React Native工程的一些知识点

1、在ReactNative中,所有的类都视为组件。

2、我们在初始化React Native新项目的时候,会有一个App.js文件。我们运行Xcode显示在屏幕上的根视图就是在这个文件中通过export default导出的那个组件。

3、在3.x的版本中,需要通过createAppContainer方法将设置好路由的导航器包装为组件。
否则报错如下:

不使用createAppContainer方法报错

4、组件的导入,在es6+中,通过import导入其他自定义组件

三、官网Demo分析

// In App.js in a new project

import React from "react";
import { View, Text } from "react-native";
import { createStackNavigator, createAppContainer } from "react-navigation";

class HomeScreen extends React.Component {
  render() {
    return (
      <View style={{ flex: 1, alignItems: "center", justifyContent: "center"   }}>
        <Text>Home Screen</Text>
      </View>
    );
  }
}

const AppNavigator = createStackNavigator({
  Home: {
    screen: HomeScreen
  }
});

export default createAppContainer(AppNavigator);

结合上面的知识点,分析如下

  • 在App.js文件中
  • 导入react-navigation
  • 定义HomeScreen组件
  • 定义导航器组件,设置路由
  • 将导航器组件包装后导出,此时导航控制器就是应用的根视图控制器

运行结果:


导航控制器

四、注意点

1、在导入navigation组件后通常需要重启终端,否则报错。

2、我们在使用WebStorm打开初始化工程项目时,都会默认导出名为App的组件

export default class App extends Component<Props> {
  render() {
    return (
      <View></View>
    );
  }
}

与直接导出 stack navigator 相比,对应用程序根部的组件进行更多的控制通常更有用,所以我们导出一个只渲染了 stack navigator 的组件。

const AppNavigator = createStackNavigator({
      Home: {
        screen: HomeScreen
      }
    });

const AppContainer = createAppContainer(AppNavigator);

export default class App extends React.Component {
   render() {
     return <AppContainer />;
   }
 }

这里需要注意,我们不可以像书写StyleSheet那样把

const AppNavigator = createStackNavigator({
      Home: {
        screen: HomeScreen
      }
    });

写到App组件的下方,这样做报错如下:


将导航器写在下方报错

3、页面组件就是创建StackNavigator配置路由的组件。只有页面组件才会拥有导航的一些属性。

页面切换

一、跳转新的页面
<Button
     title="Go to Details"
     onPress={() => this.props.navigation.navigate('Details')}
 />

如果我们使用未在 stack navigator 中定义的路由名称调用this.props.navigation.navigate 方法,则不会发生任何事情。 换句话说,我们只能导航到已经在我们的 stack navigator 上定义的路由; 不能随便导航到任意组件。

二、多次导航到同一路由
class DetailsScreen extends React.Component {
  render() {
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>Details Screen</Text>
        <Button
          title="Go to Details... again"
          onPress={() => this.props.navigation.navigate('Details')}
        />
      </View>
    );
  }
}

如果我们使用this.props.navigation.navigate导航到同一路由是不起作用的。
我们需要使用this.props.navigation.push来实现

每次调用 push 时, 我们会向导航堆栈中添加新路由。

三、总结

1、我们可以使用this.props.navigation.navigate('RouteName')跳转到在stack navigator中已经定义的路由名称对应的新页面,但要注意新路由和当前路由不相等。
2、我们可以使用this.props.navigation.navigate('RouteName')返回堆栈中的现有页面,如果有多个相同页面,则返回到最底层的页面

生命周期

React Navigation中,react的生命周期仍然适用。

只要页面处于加载的状态,react生命周期中的componentDidMountcomponentWillUnmount方法就不会被调用,这类似于iOS开发中的ViewDidLoad方法和dealloc方法,在生命周期中,只会调用一次。那么类比OC,我们如何监听ViewWillAppearViewDidAppearViewWillDisappearViewDidDisappear的方法呢?

主要有下面两种方式
1、订阅React Navigation事件
2、使用 withNavigationFocus HOC或者 <NavigationEvents />组件。

<NavigationEvents />组件为例:

假定这样一个场景:导航控制器的根视图控制器是HomeScreen,点击HomeScreen上面的按钮跳转到下一个控制器。那么我们要监听HomeScreen的四个事件。
代码如下:

class HomeScreen extends  Component{

    render() {
    return (
        <View style={styles.container}>
            <NavigationEvents
                onWillFocus={payload => console.log('will focus',payload)}
                onDidFocus={payload => console.log('did focus',payload)}
                onWillBlur={payload => console.log('will blur',payload)}
                onDidBlur={payload => console.log('did blur',payload)}
            />
            <Text>Home页面</Text>
            <Button
                title={"Go to Login"}
                onPress={()=> {
                  this.props.navigation.navigate('Details');
                }}
            />
        </View>
    )
  }
}

打印如下:


监听React Navigation事件

传递参数给路由

一、参数传递

1、使用this.props.navigation.navigate('RouteName', { /* params go here */ })
方法的第二个参数传递给路由

2、推荐传递的参数是 JSON序列化的

 this.props.navigation.navigate('Details', {
              itemId: 86,
              otherParam: 'anything you want here',
            });
二、获取参数

1、直接使用this.props.navigation.state.params进行访问,如果没有对应参数,则可能是null。
2、使用his.props.navigation.getParam进行访问

const { navigation } = this.props;
//第二个参数是设置默认值
const itemId = navigation.getParam('itemId', 'NO-ID');

3、如果你想通过 prop 直接访问 params(例如: this.props.itemId)而不是this.props.navigation.getParam,您可以使用社区开发的 react-navigation-props-mapper软件包。

4、官方Demo

class HomeScreen extends React.Component {
  render() {
    return (
       <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>Home Screen</Text>
        <Button
          title="Go to Details"
           => {
            /* 1. Navigate to the Details route with params */
            this.props.navigation.navigate('Details', {
              itemId: 86,
              otherParam: 'anything you want here',
           });
          }}
        />
      </View>
    );
  }
}

class DetailsScreen extends React.Component {
  render() {
    /* 2. Get the param, provide a fallback value if not available */
    const { navigation } = this.props;
    const itemId = navigation.getParam('itemId', 'NO-ID');
    const otherParam = navigation.getParam('otherParam', 'some default     value');

    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <Text>Details Screen</Text>
        <Text>itemId: {JSON.stringify(itemId)}</Text>
        <Text>otherParam: {JSON.stringify(otherParam)}</Text>
        <Button
          title="Go to Details... again"
          onPress={() =>
            this.props.navigation.push('Details', {
              itemId: Math.floor(Math.random() * 100),
            })}
        />
        <Button
          title="Go to Home"
          onPress={() => this.props.navigation.navigate('Home')}
        />
        <Button
          title="Go back"
          onPress={() => this.props.navigation.goBack()}
        />
      </View>
    );
  }
}

配置标题栏

一、设置不含参数的标题栏

1、每个页面组件可以有一个名为navigationOptions的静态属性,用于设置标题栏的标题使用的是title属性

class HomeScreen extends React.Component {
 static navigationOptions = {
    title: 'Home',
  };

  /* render function, etc */
}
二、设置含参数的标题栏

我们将navigationOptions作为一个函数,react会使用包含{ navigation, navigationOptions, screenProps }这些属性的对象调用它,由于传值只用到navigation,所以代码如下:

class DetailsScreen extends React.Component {
  static navigationOptions = ({ navigation }) => {
    return {
      title: navigation.getParam('otherParam', 'A Nested Details Screen'),
    };
  };

 /* render function, etc */
}

对上面提到的三个属性,解释如下:

  • navigation - 页面的 导航属性 ,在页面中的路由为navigation.state
  • screenProps - 从导航器组件上层传递的 props
  • navigationOptions - 如果未提供新值,将使用的默认或上一个选项(这里说的默认值就是指共享的navigationOptions)
三、更新含参数的标题栏

通常有必要从已加载的页面组件本身更新当前页面的navigationOptions配置。 我们可以使用this.props.navigation.setParams

  /* Inside of render() */
  <Button
    title="Update the title"
    onPress={() => this.props.navigation.setParams({otherParam: 'Updated!'})}
  />
四、调整标题样式

定制标题样式时有三个关键属性:headerStyle、headerTintColor和headerTitleStyle。

  • headerStyle:一个应用于 header 的最外层 View 的 样式对象, 如果你设置 backgroundColor ,他就是header 的颜色。

  • headerTintColor:返回按钮和标题都使用这个属性作为它们的颜色。 在下面的例子中,我们将 tint color 设置为白色(#fff),所以返回按钮和标题栏标题将变为白色。

  • headerTitleStyle:如果我们想为标题定制fontFamily,fontWeight和其他Text样式属性,我们可以用它来完成。

class HomeScreen extends React.Component {
  static navigationOptions = {
    title: 'Home',
    headerStyle: {
      backgroundColor: '#f4511e',
    },
    headerTintColor: '#fff',
    headerTitleStyle: {
      fontWeight: 'bold',
    },
  };

 /* render function, etc */
}
五、跨页面共享通用的navigationOptions

如果我们要统一配置标题栏的样式,我们只需把这些代码移动到创建导航器的代码中,放在defaultNavigationOptions属性下

class HomeScreen extends React.Component {
  static navigationOptions = {
    title: 'Home',
    /* No more header config here! */
  };

  /* render function, etc */
}

    /* other code... */

const RootStack = createStackNavigator(
  {
    Home: HomeScreen,
    Details: DetailsScreen,
  },
  {
    initialRouteName: 'Home',
    /* The header config from HomeScreen is now here */
    defaultNavigationOptions: {
      headerStyle: {
        backgroundColor: '#f4511e',
      },
      headerTintColor: '#fff',
      headerTitleStyle: {
        fontWeight: 'bold',
      },
    },
  }
);
六、覆盖共享的navigationOptions

如果在页面组件上面也指定了navigationOptions,那么将会覆盖父级的navigationOptions。

七、使用自定义组件替换标题
class LogoTitle extends React.Component {
  render() {
    return (
      <Image
        source={require('./spiro.png')}
        style={{ width: 30, height: 30 }}
      />
    );
  }
}

class HomeScreen extends React.Component {
  static navigationOptions = {
    // headerTitle instead of title
    headerTitle: <LogoTitle />,
   };

  /* render function, etc */
}

标题栏按钮

1、向标题栏中添加一个按钮
class HomeScreen extends React.Component {
  static navigationOptions = {
    headerTitle: <LogoTitle />,
    headerRight: (
      <Button
       onPress={() => alert('This is a button!')}
        title="Info"
        color="#fff"
      />
    ),
  };
}

在navigationOptions中this绑定的不是 HomeScreen 实例,所以你不能调用setState方法和其上的任何实例方法。

2、标题栏和其所属的页面之间的交互

由于上面提到的,在navigationOptions中不能使用this,所以使用getParamsetParam实现

class HomeScreen extends React.Component {
  static navigationOptions = ({ navigation }) => {
    return {
      headerTitle: <LogoTitle />,
      headerRight: (
        <Button
          onPress={navigation.getParam('increaseCount')}
          title="+1"
          color="#fff"
        />
      ),
    };
  };

  componentDidMount() {
    this.props.navigation.setParams({ increaseCount: this._increaseCount });
  }

  state = {
    count: 0,
  };

  _increaseCount = () => {
    this.setState({ count: this.state.count + 1 });
  };

  /* later in the render function we display the count */
}

下面分析一下这段代码:
1、首先按钮的点击事件我们应该传递一个函数 或者null
所以在componentDidMount方法中将参数赋值为一个函数
此时再次点击按钮的时候就会调用这个函数了。

2、React Navigation不保证屏幕组件在标题之前安装,此时按钮点击事件相当于null,不会造成异常。

Tab Navigation

在iOS开发中,最常见的UI框架是Tab中包含若干Navigation。相应的路由配置如下:

import Main from './Component/Main.js';
import Home from './Component/Home.js';
import Find from './Component/Find.js';
import Message from './Component/Message.js';
import Mine from './Component/Mine.js';

//创建navigation
const MainStack = createStackNavigator({
    main:{
        screen:Main,
    }
})

const HomeStack = createStackNavigator({
    home:{
        screen:Home,
    },
})

const FindStack = createStackNavigator({
    find:{
        screen:Find,
    }
})

const MessageStack = createStackNavigator({
    home:{
        screen:Message,
    }
})

const MineStack = createStackNavigator({
    home:{
        screen:Mine,
    }
})

//配置Tab的路由
const TabNav = createBottomTabNavigator({
    mainStack:{
        screen:MainStack,
    },
    homeStack:{
        screen:HomeStack,
    },
    findStack:{
        screen:FindStack,
    },
    messageStack:{
        screen:MessageStack,
    },
    mineStack:{
        screen:MineStack,
    }
})

const AppContainer = createAppContainer(TabNav);

在实际开发中,我们需要自定义Tab上面的样式,类似于createStackNavigator,我们可以用相同的方法配置navigationOptions。

在我们配置样式时,存在一个注意点。当我们同时使用了createStackNavigatorcreateBottomTabNavigator时,在配置样式时:如果我们想设置Navigation的样式,我们可以在createStackNavigator或者Navigator的页面元素中(就是以导航控制器为容器的那些子控制器)设置。如果我们想配置Tab的样式,我们可以在createBottomTabNavigator或者navigator(就是以Tab为容器的那些导航控制器)实例中设置。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容