高性能列表组件SectionList详解:
前言:
在上一次https://www.cnblogs.com/webor2006/p/14716461.html咱们对于高性能的VirtualizedList的列表组件进行了初步学习,其中对于列表组件回忆一下当时谈到了三种:
![](https://img-blog.csdnimg.cn/img_convert/89cd8ca93abd61f4b3e397e04920fe0b.png)
其中对于FlatList已经学习过了,接下来则来学习一下SectionList。
SectionList是一个基于VirtualizedList高性能的分组(section)列表组件,支持下面这些常用的功能:
- 完全跨平台。
- 支持水平布局模式。
- 行组件显示或隐藏时可配置回调事件。
- 支持单独的头部组件。
- 支持单独的尾部组件。
- 支持自定义行间分隔线。
- 支持下拉刷新。
- 支持上拉加载。
上面这些貌似都是FlatList的特点。。而它和FlatList不同之处在于它支持分组(section)列表的功能:
![](https://img-blog.csdnimg.cn/img_convert/ea09238b0c573b374f51c56250a5d1d9.png)
所以,如果你的列表中不需要分组(section)效果,那么就可以使用更简单的FlatList。
理论了解:
简单用例:
![](https://img-blog.csdnimg.cn/img_convert/0173665f028d97e0e38068b41c556aa5.png)
其中renderItem跟FlatList一样,必须的列表条目,而renderSectionHeader则是SectionList显示头部特有的,其中对于sections的数据格式可以看到,每一个title对应一个数组的data,表示每个分组对应的多个item。
属性:
接下来则先来对此组件的一些常用属性进行一个简单认识。
属性1:
![](https://img-blog.csdnimg.cn/img_convert/834fde7193637449932b218a33f60823.png)
用来渲染的数据,类似于中FlatList中的data属性,一般格式为:
![](https://img-blog.csdnimg.cn/img_convert/05371e85c20b8fcdb176e228d22c7457.png)
属性2:
![](https://img-blog.csdnimg.cn/img_convert/b210b997b6dd024314641fa63406c5e5.png)
用来渲染每一个section中的每一个列表项的默认渲染器。可以在section级别上进行覆盖重写。
属性3:
![](https://img-blog.csdnimg.cn/img_convert/6c784ef9a08ac57db6b8bb5c0635c446.png)
在每个section的头部渲染,在ios上,这些headers是默认粘接在ScrollView的顶部的,参见stickySectionHeadersEnabled.
属性4:
![](https://img-blog.csdnimg.cn/img_convert/801dc88f991a94daf27a29e0661fee22.png)
在等待加载新数据时将此属性设为true,列表就会显示出一个正在加载的符号。
属性5:
![](https://img-blog.csdnimg.cn/img_convert/24c9db70d65197116171d7f19144ac98.png)
如果设置了此选项。则会在列表头部添加一个标准的RefreshControl控件,以便实现“下拉刷新”的功能。同时你需要正确设置refreshing属性。
属性6:
![](https://img-blog.csdnimg.cn/img_convert/0be182f6d7b0e4e950e90cd9754e4de1.png)
当下一次section把它的前一个section的可视区推离屏幕的时候,让这个section的header粘连在屏幕的顶端。这个属性在IOS上是默认可用的,因为这是IOS的平台规范。
属性7:
![](https://img-blog.csdnimg.cn/img_convert/62b7f6b1a08d4001ae920f2b442df1fa.png)
行与行之间的分隔线组件。不会出现在第一行之前和最后一行之后。
属性8:
![](https://img-blog.csdnimg.cn/img_convert/b2ff68e051131c19cb7065216b21a8ae.png)
通过它设置尾部组件。
属性9:
![](https://img-blog.csdnimg.cn/img_convert/681ebda1a12af018f7916e2c0253b450.png)
通过它设置头部组件。
属性10:
![](https://img-blog.csdnimg.cn/img_convert/d208a94d4fce72259ffa0941ee92f4d3.png)
当列表为空时渲染,可以是一个React组件类,一个渲染函数,或一个已经渲染的元素。
属性11:
![](https://img-blog.csdnimg.cn/img_convert/6dfbc6423ff53abd7ec16a548f69af59.png)
在每个section的顶部和底部渲染(区别于ItemSeparatorComponent,它仅在列表项之间渲染)。它的作用是为了从视觉上把section与它上方或下方的headers区别开来,从这个意义上讲,它的作用和ItemSeparatorComponent是一样的,它也接受highlighted,[leading/trailing][Item/Separator]这两个默认提供的属性或其他通过separators.updateProps添加的自定义属性。
属性12:
![](https://img-blog.csdnimg.cn/img_convert/3ded279460e50e7f29b0e2762cef6376.png)
如果有除data以外的数据用在列表中(不论是用在renderItem还是Header或者Footer中),请在此属性中指定。同时此数据在修改时也需要先修改其引用地址(比如先复制到一个新的Object或者数组中),然后再修改其值,否则界面很可能不会刷新。
属性13:
![](https://img-blog.csdnimg.cn/img_convert/f5c340d26e5495936539dda5ae204a85.png)
指定一开始渲染的元素数量,最好刚刚够填满一个屏幕,这样保证了用最短的时间给用户呈现可见的内容。注意这第一批次渲染的元素不会在滑动过程中被卸载,这样是为了保证用户执行返回顶部的操作时,不需要重新渲染首批元素。
属性14:
![](https://img-blog.csdnimg.cn/img_convert/0722ae4a5b82734d0772cad18700bfa7.png)
翻转滚动方向。实质是将scale变换设置为-1。
属性15:
![](https://img-blog.csdnimg.cn/img_convert/68a2c03a9a4d5f69a8bdc1a9aad0fbde.png)
此函数用于给定的item生成一个不重复的key。Key的作用是使React能够区分同类元素的不同个体,以便在刷新时能够确定其变化的位置,减少重新渲染的开销。若不指定此函数,则默认抽取item.key作为key值。若item.key也不存在,则使用数组下标。
属性16:
![](https://img-blog.csdnimg.cn/img_convert/8dfefe6176cb30aefd9c5178c237b934.png)
当列表被滚动到距离内容最底部不足onEndReachedThreshold的距离时调用。
属性17:
![](https://img-blog.csdnimg.cn/img_convert/d021050658bb74a15eca82fd81827fc3.png)
决定当距离内容最底部还有多远时触发onEndReached回调。注意此参数是一个比值而非像素单位。比如,0.5表示距离内容最底部的距离为当前列表可见长度的一半时触发。
方法:
方法1:
![](https://img-blog.csdnimg.cn/img_convert/b6e90b1960992b9ba6555ab94a2ce417.png)
将可视区内位于特定sectionIndex或itemIndex(section内)位置的列表项,滚动到可视区的特定位置。比如说,viewPosition为0时将这个列表项滚动到可视区顶部(可能会被顶部粘接的headers覆盖),为1时将它滚动到可视区底部,为0.5时将它滚动到可视区中央。viewOffset是一个以像素为单位,到最终位置偏移距离的固定值,比如为了弥补粘接的header所占据的空间。
注意:如果没有设置getItemLayout,就不能滚动到位于外部渲染区的位置 。
方法2:
![](https://img-blog.csdnimg.cn/img_convert/5a6da2c4427ffc7f82e6afbd19750117.png)
主动通知列表发生了一个事件,以使列表重新计算可视区域。比如说当waitForInteractions为true并且用户没有滚动列表时,就可以调用这个方法,不过一般来说,当用户点击了一个列表项,或发生了一个导航动作时,我们就可以调用这个方法。
方法3:
![](https://img-blog.csdnimg.cn/img_convert/2d17963cffd23d34f95a6ade688c15cb.png)
短暂地显示滚动指示器。
具体实践:
效果:
![](https://img-blog.csdnimg.cn/img_convert/b046d8eb2f6430d31fd3a6d370fabd26.gif)
实现:
1、先运行一个项目,确保环境木问题:
由于好长一段时间木有学RN了,所以先运行确保一下环境木有问题:
![](https://img-blog.csdnimg.cn/img_convert/2257c945f22004d7ae9b10d4d6c9dd34.png)
然后运行时,果真出问题了:
TaskQueue: Error with task: undefined is not an object (evaluating '_this.view._component.measureInWindow') in react native
![](https://img-blog.csdnimg.cn/img_convert/081f679de46f879749c1a0d8dd756b3c.png)
StackOverflow上搜了一下解决方案:https://stackoverflow.com/questions/60944091/taskqueue-error-with-task-undefined-is-not-an-object-evaluating-this-view,其中说到2种解决方案:
![](https://img-blog.csdnimg.cn/img_convert/3ecb0d07213bd71219ed518e75af7f40.png)
一种是将react-navigation版本升到5.x,先看一下咱们目前用的版本:
![](https://img-blog.csdnimg.cn/img_convert/e9d4a9bcc482289a016b39e8e6d16caf.png)
用的是一个超老的版本,但是呢如果升级的话可能比较麻烦,为了不影响学习这里还是使用该版本,于是采用贴子中说的第二点方案,先找到这个文件:
![](https://img-blog.csdnimg.cn/img_convert/3ead7ef1d3e808141639c94256ed7835.png)
此时再运行就对了,但是此时会提示这么一个警告。。
![](https://img-blog.csdnimg.cn/img_convert/3a0cd364b3c4d7bd910fcd226f44926d.png)
也就是getNode()是不需要了,其实在贴子中有人已经给出相关评论了:
![](https://img-blog.csdnimg.cn/img_convert/74365e7087b41c5bc9db7e1b23a686b0.png)
所以,此时将getNode()去掉了:
![](https://img-blog.csdnimg.cn/img_convert/19d103f528fb2952518a2186494b5555.png)
再运行就没报这种警告了,只是还有其它警告,这里先忽略了,先往前学着:
![](https://img-blog.csdnimg.cn/img_convert/2ef6ec7e8234e5952f8aec7f073940fd.png)
1、新建页面,添加测试入口:
由于跟FlatList用法差不多,直接基于它拷贝一个新页面:
![](https://img-blog.csdnimg.cn/img_convert/12ce7de6ce3608466c538191242d1be4.png)
然后增加一个跳转到此页面的测试入口:
![](https://img-blog.csdnimg.cn/img_convert/9c8a620fd53709e881e6bee7d58a8437.png)
然后在App.js中增加一个测试按钮:
![](https://img-blog.csdnimg.cn/img_convert/aeca0122ec108b7ed53b16d65d64a74c.png)
此时运行看一下:
![](https://img-blog.csdnimg.cn/img_convert/c64bf03b57098db75d8cee6cd69282e4.gif)
2、准备数据源:
![](https://img-blog.csdnimg.cn/img_convert/aa8d96e4102d4ff4007d037f68693c87.png)
此时运行看一下效果:
![](https://img-blog.csdnimg.cn/img_convert/cae3cdd399a799cc2333fd318ec3f3b8.png)
此时的效果跟FlatList木有啥区别,因为还有属性没有设置。
3、设置分组标题:renderSectionHeader:
如上面属性介绍:
![](https://img-blog.csdnimg.cn/img_convert/7247a323d0a59d7973f0507148d03477.png)
所以来设置一下:
![](https://img-blog.csdnimg.cn/img_convert/cfe276ee07d7eb2f07b376ece75dfe97.png)
其中涉及到一个样式:
![](https://img-blog.csdnimg.cn/img_convert/1ec476d960a9e8ab10d2d10fa44ab232.png)
运行发现在android和ios上的表现是有些差别的,先来看ios的效果:
![](https://img-blog.csdnimg.cn/img_convert/3062445c6458a17034695966c280f975.gif)
其中可以看到有一个悬浮的效果,但是呢到了android上面就没有悬浮了:
![](https://img-blog.csdnimg.cn/img_convert/b3d61d43549c452dcae03ff7a457191e.gif)
这可能是平台的差异吧,目前每个Item太高了,这里将其margin取消掉之后再运行一下:
![](https://img-blog.csdnimg.cn/img_convert/8231f24e86de257229ad261d2eadf89d.png)
![](https://img-blog.csdnimg.cn/img_convert/ed42b7eda6ac18b576ea2ec0193a97a4.png)
此时再看一下效果:
![](https://img-blog.csdnimg.cn/img_convert/518176aac2c86c5a3809f2df16e288dd.gif)
4、设置分隔线:
先来改一下item的背景色为白色,设置的目的是为了暴露分隔线问题:
![](https://img-blog.csdnimg.cn/img_convert/403585859f3a8ace6db252cc52dcf6cf.png)
此时的效果如下:
![](https://img-blog.csdnimg.cn/img_convert/5823be579e9119bf81bdd7f528a49bc7.png)
是不是item与item差了分隔线了,所以接下来设置一下,如之前属性所示:
![](https://img-blog.csdnimg.cn/img_convert/e8a789efc5ec3bec19e38d3836665b6f.png)
![](https://img-blog.csdnimg.cn/img_convert/7f47cea32ff4ff22910f3703e72fb643.png)
![](https://img-blog.csdnimg.cn/img_convert/5ea0d4512c3ce2bab0b4fde6b5de4044.png)
此时看效果:
![](https://img-blog.csdnimg.cn/img_convert/60d84f24fa46a062237479698b2f60b9.png)
5、下拉刷新处理:
接下来处理下拉刷新逻辑,首先将数据源写活,目前我们是写死成了静态数据了:
![](https://img-blog.csdnimg.cn/img_convert/1cdeb7b32f3e3a6f7fcb57dc04e97276.png)
改成:
![](https://img-blog.csdnimg.cn/img_convert/948409bc62fdb183f927ac20fcd4b7b3.png)
如果你觉得在访问state中的数据时每次都需要用"this.state.xxx"来访问比较麻烦,此时解构的场景就出来了,也是经常会用到的一个小技巧,如下:
![](https://img-blog.csdnimg.cn/img_convert/dec13a20234d88342c597773d1c75d0d.png)
运行:
![](https://img-blog.csdnimg.cn/img_convert/a6a9c353324429a4a52e2cdb2aab6d44.gif)
其中为啥下拉刷新之后会显示这些数据,这里简单再回忆一下上次写的加载的逻辑:
![](https://img-blog.csdnimg.cn/img_convert/88930221146daaffda7a1336e5be423b.png)
以上就是SectionList的基本用法,一些其它用法可以之后再慢慢挖掘。
react-navigation:
了解前世今生:
概述:
还记得我们入口页面的导航用的是哪个组件么,瞅一下我们的代码:
![](https://img-blog.csdnimg.cn/img_convert/dae5fd1001d859dd567ed86702eeeca5.png)
是的,react-navigation导航器组件,接下来则来对它进行一个整体的认识:导航器是做APP开发所不可或缺的一个组件,现在RN生态中最主流的导航器当react-navigation莫属了。RN发展初期有个名叫Navigator的导航器,但因为其功能单一,RN社区便开始组织构建了一个功能更强大的导航器便是react-navigation了,随着react-navigation逐渐稳定,Navigator也被光荣的退休了。
react-navigation的出现替代了Navigator、Ex-Navigation等老一代的导航组件,react-navigation可以说是Navigator的加强版,不仅有Navigator的全部功能,另外还支持底部导航类似于与IOS中的UiTabBarController,此外它也支持侧拉效果方式的导航类似于Android中的抽屉效果。
react-navigation发展到现在已经经历了1x,2x,3x,4x,5x【目前5x先不关注,先跟着教程的节奏】的版本,目前使用不同版本react-navigation的开发者都有,了解react-navigation不同版本的差异和功能有助于我们进行导航器的开发和应用。
各react-navigation版本的差异:
![](https://img-blog.csdnimg.cn/img_convert/9dfaf0486206057e80aa62b66c0ae85e.png)
看一下咱们目前使用的版本:
![](https://img-blog.csdnimg.cn/img_convert/2ff3ebf72396d6c3b339563c40c3795d.png)
。。老古董了,所以正好借此机会对它有一个整体的了解,以便为以后升级做准备。
在了解了各版本的差异之外,接下来则来了解一下什么是导航器以及它们的通用API,依然照着大神的这篇https://www.devio.org/2018/12/15/react-navigation3x/来过一遍【过一遍的作用是强制让他人的知识点为自己所用】。
什么是导航器?
导航器可以看成是一个普通的React组件,你可以通过导航器来定义你的APP的导航结构。导航器还可以渲染通用元素,例如可以配置的标题栏和选项卡栏。
在react-navigation中常用的导航器有以下7种【可能现在要多于它,但是不管这么多,先保持跟学习教程的一个同步节奏,学习节奏还是很重要的】:
- createStackNavigator: 类似于普通的Navigator,屏幕上方导航栏;【最常用】
- createTabNavigator: createTabNavigator已弃用,使用createBottomTabNavigator和/或createMaterialTopTabNavigator替代;
- createBottomTabNavigator:相当于iOS里面的TabBarController,屏幕下方的标签栏;
- createMaterialTopTabNavigator:屏幕顶部的材料设计主题标签栏;
- createDrawerNavigator: 抽屉效果,侧边滑出;
- createSwitchNavigator:SwitchNavigator 的用途是一次只显示一个页面。
你可以通过以上7种导航器来创建你APP,可以是其中一个也可以多个组合,这个可以根据具体的应用场景并结合每一个导航器的特性进行选择。
在开始学习7种导航器之前,我们需要先了解两个和导航关于概念:
Screen navigation prop(屏幕导航属性)
:通过navigation可以完成屏幕之间的调度操作,例如打开另一个屏幕;Screen navigationOptions(屏幕导航选项)
: 通过navigationOptions可以定制导航器显示屏幕的方式(例如:头部标题,选项卡标签等);
简单使用示例:
const SomeNav = createStackNavigator/createBottomTabNavigator/createMaterialTopTabNavigator/createDrawerNavigator/createSwitchNavigator({
// config
});
<SomeNav
screenProps={xxx}
ref={nav => { navigation = nav; }}
onNavigationStateChange=(prevState, newState, action)=>{
}
/>
- ref:可以通过
ref
属性获取到navigation
; - onNavigationStateChange(prevState, newState, action):顶级节点除了
ref
属性之外,还接受onNavigationStateChange(prevState, newState, action)
属性,每次当导航器所管理的state
发生改变时,都会回调该方法;- prevState:变化之前的state;
- newState:新的state;
- 导致state变化的action;
- screenProps:向子屏幕传递额外的数据,子屏幕可以通过this.props.screenProps获取到该数据。
Screen Navigation Prop(屏幕的navigation Prop):
当导航器中的屏幕被打开时,它会收到一个navigation
prop,navigation
prop是整个导航环节的关键一员,接下来就详细讲解一下navigation
的作用。
navigation包含以下功能:
- navigate:跳转到其他界面;【咱们目前采用的就是它】
- state:屏幕的当前state;
- setParams:改变路由的params;
- goBack:关闭当前屏幕;
- dispatch:向路由发送一个action;
- addListener:订阅导航生命周期的更新;
- isFocused:true 标识屏幕获取了焦点;
- getParam:获取具有回退的特定参数;
- dangerouslyGetParent:返回父导航器;
其中看一下我们代码中的路由跳转就是利用的navigation:
![](https://img-blog.csdnimg.cn/img_convert/03bb4a53baaf135c7b33729aa3d24358.png)
注意:一个navigation有可能没有navigate、setParams以及goBack,只有state与dispatch,所以在使用navigate时要进行判断,如果没有navigate可以使用navigation去dispatch一个新的action。如:
const {navigation,theme,selectedTab}=this.props;
const resetAction = StackActions.reset({
index: 0,
actions: [
NavigationActions.navigate({
routeName: 'HomePage',
params:{
theme:theme,
selectedTab:selectedTab
},
})
]
})
navigation.dispatch(resetAction)
提示:这里的reset
在2.0及以后版本中被从NavigationActions中移到了StackActions
中,使用时记得留意。
StackNavigator的navigation的额外功能:
当且仅当当前 navigator 是 stack navigator 时【在前面不是说了创建navigotor有很多方式,其实就有一个createStackNavigator,就是指这种场景】,this.props.navigation
上有一些附加功能。 这些函数是 navigate 和 goBack 的替代方法, 你可以使用任何你喜欢的方法。 这些功能是:
- this.props.navigation
push - 导航到堆栈中的一个新的路由
pop - 返回堆栈中的上一个页面
popToTop - 跳转到堆栈中最顶层的页面
replace - 用新路由替换当前路由
reset - 擦除导航器状态并将其替换为多个操作的结果
dismiss - 关闭当前栈
使用navigate进行界面之间的跳转:
在进行页面跳转时,我们会调用navigate方法对吧:
![](https://img-blog.csdnimg.cn/img_convert/6622834bf8e71b2153df49cbd74d1cfa.png)
下面来学习一下它的用法:navigation.navigate({routeName, params, action, key})
或 navigation.navigate(routeName, params, action)
- routeName:要跳转到的界面的路由名,也就是在导航其中配置的路由名;
- params:要传递给下一个界面的参数;
- action:如果该界面是一个navigator的话,将运行这个sub-action;
- key:要导航到的路由的可选标识符。 如果已存在,将后退到此路由;
比如:
export const AppStackNavigator = createStackNavigator({
HomeScreen: {
screen: HomeScreen
},
Page1: {
screen: Page1
})
class HomeScreen extends React.Component {
render() {
const {navigate} = this.props.navigation;
return (
<View>
<Text>This is HomeScreen</Text>
<Button
onPress={() => navigate('Page1', {name: 'Devio'})}
title="Go to Page1"
/>
</View>
)
}
}
使用state的params:
在跳转路由的时候,不是可以携带参数么?比如上面的例子中的:
![](https://img-blog.csdnimg.cn/img_convert/99f28b30a2e153ab5b71d80f826c7b68.png)
所以这里就来学习一下参数使用的问题,其实也很简单,可以通过this.props.state.params来获取通过setParams()
,或navigation.navigate()
传递的参数。比如:
![](https://img-blog.csdnimg.cn/img_convert/7266a563007ee90b702344ca7c7c10b7.png)
此时在页面中接收参数则需要这么写:
![](https://img-blog.csdnimg.cn/img_convert/bfe2a2263ddcc2ef94c023364fb702a4.png)
需要一层层解构。
使用setParams 改变route params:
setParams: function setParams(params)
: 我们可以借助setParams
来改变route params,比如,通过setParams
来更新页面顶部的标题,返回按钮等;
![](https://img-blog.csdnimg.cn/img_convert/2c748be8655d101022a6e6fc77d9b03c.png)
注意:navigation.setParams改变的是当前页面的Params,如果要改变其他页面的Params可以通过NavigationActions.setParams完成,之后会学习到。
使用goBack返回到上一页面或指定页面:
goBack: function goBack(key)
:我们可以借助goBack
返回到上一页或者路由栈的指定页面。
- 其中
key
表示你要返回到页面的页面标识如id-1517035332238-4
,不是routeName。 - 可以通过指定页面的
navigation.state.key
来获得页面的标识。 - key非必传,也可传null,如果是返回上一页,此key可不传。
navigation.state {params: {…}, key: "id-1517035332238-4", routeName: "Page1"}
也就是说,我们可以通过navigation.state中来获取key值,如下:
export default class Page1 extends React.Component {
render() {
const {navigation} = this.props;
return <View style=>
<Text style={styles.text}>欢迎来到Page1</Text>
<Button
title="Go Back"
onPress={() => {
navigation.goBack();
}}
/>
</View>
}
}
通过dispatch发送一个action:
还记得上面说过这么一个东东么,回忆一下:
![](https://img-blog.csdnimg.cn/img_convert/2a4f764fefcd582e993f5168536a015f.png)
接下来就来了解这个dispatch:
dispatch: function dispatch(action)
:给当前界面设置action,会替换原来的跳转,回退等事件。
那都能发送哪些Action呢?下面就来瞅一下。
1、NavigationActions:
这个Actions涉及到如下API:
- Navigate : 导航到其他的页面;
- Back : 返回到上一个页面;
- Set Params : 设置指定页面的Params;
- Init : 初始化一个 state 如果 state 是 undefined;
下面具体来看一下这些api:
a、Navigate :
Navigatie action会使用Navigate action的结果来更新当前的state。
方法原型:navigate({routeName, params, action, key})
- routeName:字符串,必选项,在app的router里注册的导航目的地的routeName。
- params:对象,可选项,融合进目的地route的参数。
- action:对象,可选项(高级),如果screen也是一个navigator,次级action可以在子router中运行。在文档中描述的任何actions都可以作为次级action。
- key:
string or null
可选,要导航到的路由的标识符。如果已存在, 则导航回此路由。
import { NavigationActions } from 'react-navigation'
const navigateAction = NavigationActions.navigate({
routeName: 'Profile',
params: {},
action: NavigationActions.navigate({ routeName: 'SubProfileRoute'})
})
this.props.navigation.dispatch(navigateAction)
上面代码的意思是跳转到Profile页面,其参数为空,另外还有一个子action。
b、Back :
返回到前一个screen并且关闭当前screen.backaction creator接受一个可选的参数:
方法原型:back(key)
- key:
String
可选,这个可以和上文中讲到的goBack的key是一个概念;
import { NavigationActions } from 'react-navigation'
const backAction = NavigationActions.back();
this.props.navigation.dispatch(backAction);
c、Set Params :
通过SetParams我们可以修改指定页面的Params。
- params:对象,必选参数,将会被合并到已经存在页面的Params中。
- key:字符串,必选参数,页面的key。
import { NavigationActions } from 'react-navigation'
const setParamsAction = NavigationActions.setParams({
params: { title: 'HomePage' },
key: 'id-1517035332238-4',
});
这里有一个小疑问:navigation中其实已经有setParams了为什么还要有NavigationActions.setParams呢?其实原因有两点:
- 在上文中讲到过navigation中有可能只有state与dispatch,这个时候如果要修改页面的Params,则只能通过
NavigationActions.setParams
了; - 另外,navigation.setParams只能修改当前页面的Params,而
NavigationActions.setParams
可以修改所有页面的Params;
2、StackActions:
该Actions有如下API:
- Reset : 重置当前 state 到一个新的state;
- Replace : 使用另一个路由替换指定的路由;
- Push : 在堆栈顶部添加一个页面,然后跳转到该页面;
- Pop : 跳转到上一个页面;
- PopToTop : 跳转到堆栈最顶层的页面,并销毁其他所有页面;
下面一一来看一下。
a、Reset:
Reset action删掉所有的navigation state并且使用这个actions的结果来代替。
- index,数组,必选,navigation state中route数组中激活route的index。
- actions,数组,必选项,Navigation Actions数组,将会替代navigation state。
- key:
string or null
可选, 如果设置,具有给定 key 的导航器将重置。 如果为null,则根导航器将重置。
import { NavigationActions, StackActions } from 'react-navigation'
const resetAction = StackActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'Profile'})
]
})
this.props.navigation.dispatch(resetAction)
此代码的意思是恢复第0的navigation的状态,并且用Profile这个路由来代替。使用场景:比如app从Splash进入了首页,然后此时按back键是不想再回到了Splash页,这时可以使用NavigationActions.reset
重置它。
index参数被用来定制化当前激活的route。举个例子:使用两个routes WelcomePage和HomePage给一个基础的stack navigation设置。为了重置route到HomePage,但是在堆栈中又存放在WelcomePage之上,你可以这么做:
import { NavigationActions, StackActions } from 'react-navigation'
const resetAction = StackActions.reset({
index: 1,
actions: [
NavigationActions.navigate({ routeName: 'WelcomePage'}),
NavigationActions.navigate({ routeName: 'HomePage'})
]
});
this.props.navigation.dispatch(resetAction);
说实话,目前还不晓得上面说的是啥意思,木关系,先笔记,待未来学习实践到这块再回过头来复习,自然就明白了。
b、Replace:
Replace - 用另一个路由替换指定的路由
- key - string - 被替换的路由的 key,如果未指定,最近的路由将会被替换
- newKey - string - 用于替换路线的 Key。 如果未提供,则自动生成。
- routeName - string - routeName用于替换路由。
- params - object - 要传入替换路由的参数。
- action - object - 可选的子动作。
- immediate* - boolean - 目前没有效果, 这是 stack navigator 支持动画替换(它目前不支持)的占位符。
c、Push:
Push - 在堆栈顶部添加一条路由,并导航至该路由. 与navigate的区别在于,如果有已经加载的页面,navigate方法将跳转到已经加载的页面,而不会重新创建一个新的页面。 push 总是会创建一个新的页面,所以一个页面可以被多次创建
- routeName - string - routeName用于替换路由。
- params - object - 将合并到目标路由的参数(通过this.props.navigation.state.params在目标路由获取)。
- action - Object - 可选 - (高级)如果页面是 navigator,则是在子路由器中运行的子操作。
比如:
import { StackActions } from 'react-navigation';
const pushAction = StackActions.push({
routeName: 'Profile',
params: {
myUserId: 9,
},
});
this.props.navigation.dispatch(pushAction);
d、Pop:
The pop 一个可以返回到堆栈中上一个路由到方法,通过设置参数 n,可以指定返回的多少层。
- n - number - 返回的层数
import { StackActions } from 'react-navigation';
const popAction = StackActions.pop({
n: 1,
});
this.props.navigation.dispatch(popAction);
e、PopToTop:
popToTop 一个可以直接跳转到堆栈最顶层,并销毁其它所有页面的方法,它在功能上与StackActions.pop({n:currentIndex})
相同。
import { StackActions } from 'react-navigation';
this.props.navigation.dispatch(StackActions.popToTop());
其实根据这些api的英文也大致知道是个啥意思,有点类似于Android Activity Stack。
安装使用指南:
上面理论完了之后,接下来则来使用一下它,先来上官网看一下:https://reactnavigation.org/
![](https://img-blog.csdnimg.cn/img_convert/7b30526bd43bcd6530444d65a45a2692.png)
其中最新版已经到了6.x, 然而准备要演练的却是4.x的,好low呀,low就low吧,反正学习能力也有限,不管哪个技术都会有版本的问题,踏实先学好一个版本之后,未来对于新版的使用也比较简单,思想其实都是可以借鉴的,还是那句话,任何一个知识的学习都不会是孤立的,另外一个重要原因是为了跟所学习的教程保持一个较好的节奏。另外借鉴一下官网的两张图可以非常直观的了解React Navigation的用途:
![](https://img-blog.csdnimg.cn/img_convert/bdec4f18fc619579f71acbdd206bfcf0.png)
![](https://img-blog.csdnimg.cn/img_convert/db87b4147761e9c233f63be2cbf98a84.png)
相当形象。
4x版本的大特性:
navigator被拆分到不同的库中了:
![](https://img-blog.csdnimg.cn/img_convert/2d064c4a2ea19930d0fd5086d67a75e3.png)
所以,用到这些类型导航器的地方其引入方式也需要做相应的调整:
![](https://img-blog.csdnimg.cn/img_convert/938fdbc20f97e68fb29d8775974ebe59.png)
4x版本安装使用的正确方式:
4x版本对于的RN版本是0.6x,使用需要注意,目前我们使用的RN版本刚好就是它:
![](https://img-blog.csdnimg.cn/img_convert/3b776c082297baf2fd320330162b445f.png)
接下来则来使用吧。
第一步:新建一个RN DEMO工程
react-native init react_navigation_demo --version 0.62.2
![](https://img-blog.csdnimg.cn/img_convert/34721927ea77476fdf08138c02f71a8e.png)
然后为啥要指定0.62.2这个版本就不过多说明了,可以参考https://www.cnblogs.com/webor2006/p/14716461.html:
![](https://img-blog.csdnimg.cn/img_convert/f64aed1bc658f618ef96fe15d201dc25.png)
然后先运行一下,确保环境都ok,然后用webstorm打开工程:
![](https://img-blog.csdnimg.cn/img_convert/cb73494aee420f3670ae76a1d751187b.png)
第二步:安装主库
![](https://img-blog.csdnimg.cn/img_convert/e4e710e81692e66581dc2f752948de35.png)
![](https://img-blog.csdnimg.cn/img_convert/71200c426af7fbf1db6abb4fc94c5933.png)
第三步:安装主库依赖的三方库
因为新版react-navigation依赖一些第三方库,所以安装时需要同时引入:
![](https://img-blog.csdnimg.cn/img_convert/f300fe7b9b559bfd7a4af505a242ab23.png)
![](https://img-blog.csdnimg.cn/img_convert/ab877a377cfc19695064a035df68a743.png)
![](https://img-blog.csdnimg.cn/img_convert/137738953e304d1f66f824771dd593d3.png)
![](https://img-blog.csdnimg.cn/img_convert/23328c51efa6204baeb7e89d4d2daa32.png)
![](https://img-blog.csdnimg.cn/img_convert/2a08abf30f6baf303e083bc937c7eef2.png)
第四步:根据需要引入各导航组件的库
![](https://img-blog.csdnimg.cn/img_convert/9c205f78e9f1148d407a1e2b8950608f.png)
在上面不是说明了在4.x时navigator被拆分到不同的库中了么?
![](https://img-blog.csdnimg.cn/img_convert/e3972d94e0600c60f72eaa1270713ca1.png)
所以,需要都导入一下:
![](https://img-blog.csdnimg.cn/img_convert/eacaff1279f4b94acf8eec52179684ee.png)
第四步:执行pob install
为了在ios上完成安装,还需要执行如下命令:
![](https://img-blog.csdnimg.cn/img_convert/c08de2a604b1e6e15677b4f6563c63ca.png)
第五步:配置react-native-gesture-handler:
为了在Android上能够使react-native-gesture-handler有效,需要修改MainActivity.java,它主要是完成导航器手势滑动过程中手势的处理拦截之类的:
![](https://img-blog.csdnimg.cn/img_convert/17210a4368da49a52d693a168fe32962.png)
修改如下:
![](https://img-blog.csdnimg.cn/img_convert/618d76999fa899d97831c0fbf3a37d2f.png)
package com.react_navigation_demo;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactRootView;
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript. This is used to schedule
* rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "react_navigation_demo";
}
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected ReactRootView createRootView() {
return new RNGestureHandlerEnabledRootView(MainActivity.this);
}
};
}
}
第六步:运行android和ios
此时为了确保react-navigation环境的安装,需要在android和ios都运行一下,如果没有报错正常运行那么恭喜,咱们的react-navigation环境就已经配置完成了,接下来就可以开启react-navigation学习之旅啦。
已知兼容问题:
当你在RN 0.61.0上使用react-navigation 4.0时你会发现,createMaterialTopTabNavigator在Android上左右滑动无效,这是一个已知的兼容问题。
该问题由“react-native”:“0.61.0-rc.0”的ReactActivityDelegate的改动引起,导致protected ReactRootView createRootView()不回调,所以react-navigation所使用的react-native-gesture-handler也就在android上无法监测到用户的手势,进而导致滑动切换tab失效。解决办法可以使用0.60.5版本的rn。
其它升级适配说明:
react-native中的AsyncStorage在未来的版本迭代中将会被从react-native中移除,官方推荐使用react-native-community/async-storage来代替:
安装方式 :
![](https://img-blog.csdnimg.cn/img_convert/5a20c0dc75d71d5e9e124b6fc64ba30d.png)
然后:
![](https://img-blog.csdnimg.cn/img_convert/fcbddfdf9d7bd7cc630c696e9a6247f9.png)
使用方式:
![](https://img-blog.csdnimg.cn/img_convert/59969a716b18b054cccc90f0a03b7b9f.png)
其他API不变。
FAQ:
1、Cannot get property 'supportLibVersion' on Extra properties extension as it does not exist.
问题描述:
升级到rn 0.61.0后报上述错误
解决方法:
在android/build.gradle中添加supportLibVersion:
![](https://img-blog.csdnimg.cn/img_convert/020e8547991903421e2e285de0b75a33.png)
2、Unsupported Modules Detected
问题描述:
![](https://img-blog.csdnimg.cn/img_convert/f2596eccfe2db31677c62c5caec9c8e9.png)
解决办法:
删除Android下的./idea目录然后重新打开项目。
3、Android不显示矢量图标【关于矢量图标下面就会学到】
问题描述:
版本信息:
![](https://img-blog.csdnimg.cn/img_convert/998d0bf069110a57dba976fb1dc5822f.png)
不报错但是不显示图标。
解决办法:
在android/app/build.gradle添加:
![](https://img-blog.csdnimg.cn/img_convert/10eb5b3c08761e2b5562a637d2031401.png)
4、Update is invalid - A JS bundle file named "null"
问题描述:
![](https://img-blog.csdnimg.cn/img_convert/c3d7dd3e7bcec27a7cc2535612e7c6e2.png)
Codepush jsbundle下载完成,更新时报错,如下:
![](https://img-blog.csdnimg.cn/img_convert/6328d08b96e25d9bdbe8dc8e9248d133.png)
解决办法:
在MainApplication.java中添加:CodePush.getJSBundleFile():
![](https://img-blog.csdnimg.cn/img_convert/4100f5f819af27c4d6d498767a328f6b.png)
5、Error in getting binary resources modified time
问题描述:
![](https://img-blog.csdnimg.cn/img_convert/081f3abbd1d99a7879cc9c6c6cebcc8b.png)
Codepush检测更新,logcat上述错误
解决办法:
在android/app/build.gradle添加:
![](https://img-blog.csdnimg.cn/img_convert/43ea8ebf8bdc89dd9f02070caaab3d0a.png)
6、Unrecognized font family 'Ionicons' or 'Materiallcons'
问题描述:
启动IOS APP报错,无法加载字体文件:
版本信息:
![](https://img-blog.csdnimg.cn/img_convert/4710b1ead75f65c4631fae72c96ce670.png)
无论是自动link还是手动link都会报这个错误。
解决办法:
第一步:切到ios目录执行install:
![](https://img-blog.csdnimg.cn/img_convert/96af4a2ef69f6d35af60e5d26823cef7.png)
第二步:将下面配置粘贴到info.plist:
![](https://img-blog.csdnimg.cn/img_convert/16187ab500b09301d674dfc42b75d772.png)
然后重新运行项目,如果问题依旧,用Xcode打开Github_RN.xcworkspace,然后改为用Xcode运行项目。
矢量图标:
在上面也提到了矢量图标,如今它在APP中的图标使用也越来越流行了,所以如何在RN中使用矢量图标也是一个刚需。
安装矢量图标:
它的github为:https://github.com/oblador/react-native-vector-icons
![](https://img-blog.csdnimg.cn/img_convert/b80ac759ebc488bd08f5fb2187f2cbd4.png)
安装方式如下:
![](https://img-blog.csdnimg.cn/img_convert/f861a304a19b0e3885c2385ccd66ae94.png)
咱们是用的yarn,所以添加如下:
![](https://img-blog.csdnimg.cn/img_convert/162c7475069988428051df91af40eb2e.png)
![](https://img-blog.csdnimg.cn/img_convert/07b43627fefb5dd25c4e2ccd3d338166.png)
ios配置:
具体官网都有说明,这里直接来配置了:
1、Info.plist中增加如下配置:
<key>UIAppFonts</key>
<array>
<string>AntDesign.ttf</string>
<string>Entypo.ttf</string>
<string>EvilIcons.ttf</string>
<string>Feather.ttf</string>
<string>FontAwesome.ttf</string>
<string>FontAwesome5_Brands.ttf</string>
<string>FontAwesome5_Regular.ttf</string>
<string>FontAwesome5_Solid.ttf</string>
<string>Foundation.ttf</string>
<string>Ionicons.ttf</string>
<string>MaterialIcons.ttf</string>
<string>MaterialCommunityIcons.ttf</string>
<string>SimpleLineIcons.ttf</string>
<string>Octicons.ttf</string>
<string>Zocial.ttf</string>
<string>Fontisto.ttf</string>
</array>
![](https://img-blog.csdnimg.cn/img_convert/757be331c5f8f0536fe7e6e6aa3a8648.png)
![](https://img-blog.csdnimg.cn/img_convert/d61ef3eb9727c9498dc6e9721d6e3b9a.png)
2、切到ios目录执行install:
![](https://img-blog.csdnimg.cn/img_convert/b3421ec86e9efe07f58c5fdb3892aa6d.png)
android配置:
1、android/app/build.gradle进行配置:
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
![](https://img-blog.csdnimg.cn/img_convert/30fbabd4ca72f68872732a59f468c754.png)
项目中使用矢量图标:
对于支持的矢量图标,可以上这个官网去查找:https://oblador.github.io/react-native-vector-icons/
![](https://img-blog.csdnimg.cn/img_convert/7136c8bb9c243df354d34cbd072bc720.png)
其中可以看到有很多分类:
![](https://img-blog.csdnimg.cn/img_convert/f0389338d877c5e26301f66c051ba11c.png)
![](https://img-blog.csdnimg.cn/img_convert/342dd7885059e3aa6f33be8e03efa7a9.png)
![](https://img-blog.csdnimg.cn/img_convert/d5edf974c3c502b8198b4aee5ac24111.png)
比如我们要使用AntDesign这个类中的图标库,可以在项目中这样导入:
![](https://img-blog.csdnimg.cn/img_convert/93fe6834f870f60f30eea956b9ed2284.png)
然后接下来就可以使用了,这里以两个图标为例:
![](https://img-blog.csdnimg.cn/img_convert/fd950c1f3badc5182289bebaf6f63056.png)
![](https://img-blog.csdnimg.cn/img_convert/a956a2b98b2f1143f80ef805e4d50b8e.png)
![](https://img-blog.csdnimg.cn/img_convert/60b7dc87709ce75e92cf423d7b859ba6.png)
运行:
先看一下android的效果:
![](https://img-blog.csdnimg.cn/img_convert/c35de8d5e9ba9e5f62ffe4c9cc6438e1.png)
再来看一下Ios上的效果,运行发现报错了。。
![](https://img-blog.csdnimg.cn/img_convert/6755241d97082d164cc92d5013219a94.png)
度娘上看到一篇文章:https://blog.csdn.net/ioth5/article/details/104253511,试试:
![](https://img-blog.csdnimg.cn/img_convert/8deb7bc3e30c767f9d1310cd487b3630.png)
再次运行看一下:
![](https://img-blog.csdnimg.cn/img_convert/50a033db002f96a9aa895272b0fe24b7.png)
嗯,跟android的效果一样。
总结:
关于导航器咱们这里只是搭建了一个基础环境,正式的使用待下次继续,另外由于每个人电脑的环境可能不一样,所以其运行出来可能不一定都一样,当然也有可能遇到不同样的问题,自行上网解决既可,学习本来就是模仿+超越的过程【对于我而言,当然对于纯大佬那直接超越就可以了~~】,模仿中遇到的各种问题正好是加深学习的一个很好的过程,篇幅有限,下次继续~~