美文网首页
react-navigation的screen参数使用高阶组件时

react-navigation的screen参数使用高阶组件时

作者: 黑哥_2c57 | 来源:发表于2019-05-23 16:43 被阅读0次

    场景:

    react-native react-navigation @react-native-community/netinfo

    需求:

    假设我的底部导航有三个页面,每个页面需要在进入前都检查当前是否联网,没有联网的情况下,显示另一个页面,只有在有网络的情况下再进入响应的页面。


    image.png

    实现一:

    先写公共的网络跑丢的页面,然后在每个页面进行逻辑判断, 不同情况显示不同页面,这个当然可以实现,但是逻辑判断那部分每个页面都写一次,就重复了,这个就不写代码了,更好的方式是使用高阶组件修饰,请看实现二。

    实现二:

    先写一个WithNoNet的高阶组件, 然后在每个页面中使用WithNoNet修饰一下,实现将这部分判断逻辑抽离出来,简洁优雅高效。

    //WithNoNet高阶组件
    import React from 'react'
    import NetInfo from "@react-native-community/netinfo";
    import NoNetwork from '../common/NoNetwork'
    
    export default WrappedComponent => {
        return class extends React.Component {
            constructor(props) {
                super(props)
                this.state = {
                    isConnected: false,
                    hasCheckedNetwork: false, //为了解决执行网络检查时,NoNetwork页面一闪而过的现象
                }
            }
    
            async componentDidMount(): void {
                await this.checkNetwork()
            }
    
            checkNetwork = async () => {
                try {
                    let netInfo = await NetInfo.fetch()
                    if (netInfo.isConnected) {
                        this.setState({
                            isConnected: true,
                            hasCheckedNetwork: true,
                        })
                    } else {
                        this.setState({
                            hasCheckedNetwork: true,
                        })
                    }
                } catch (e) {
                    console.log(e)
                }
            }
    
            render() {
                const {isConnected, hasCheckedNetwork} = this.state
                if (isConnected) {
                    return <WrappedComponent />
                } else {
                    if (hasCheckedNetwork) {
                        return <NoNetwork checkNetwork={this.checkNetwork}/>
                    } else {
                        return null
                    }
                }
            }
        }
    }
    

    Home主页使用WithNoNet修饰

    //Home主页使用WithNoNet修饰
    import React from 'react'
    import { View,Text} from 'react-native'
    import WithNoNet from '../WithNoNet'
    class Home extends React.Component {
        componentDidMount(){
              console.log(this.props.navigation)    
        }
        render() {
            return (
                <View>
                <Text> This is home </Text>
                </View>
            )
        }
    }
    export default WithNoNet(Home)
    

    现在开始进入本文主题

    这样修饰了之后,一看没啥问题,但是我一运行就出问题了, 因为Home组件的props中的navigation成了undefined,检查我的底部导航组件

    import React from 'react'
    import {createBottomTabNavigator, createAppContainer} from 'react-navigation'
    import Base from '../page/Base'
    import My from '../page/My'
    import Ionicons from 'react-native-vector-icons/Ionicons'
    import Home from '../page/Home/Home'
    import Alert from '../page/Alert'
    import {ACTIVATE_COLOR} from "../config/constant";
    import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
    import FontAwesome5 from 'react-native-vector-icons/FontAwesome5'
    const bottomNavigator = createBottomTabNavigator({
            Home: {
                screen:Home,
                navigationOptions: {
                    tabBarLabel: "首页",
                    tabBarIcon: ({tintColor, focused}) =>
                        <Ionicons
                            name={'md-home'}
                            size={20}
                            color={tintColor}
                        />
                }
            },
            Alert: {
                screen:Alert,
                navigationOptions: {
                    tabBarLabel: "告警",
                    tabBarIcon: ({tintColor, focused}) =>
                        <MaterialCommunityIcons
                            name={'alert-decagram'}
                            size={20}
                            color={tintColor}
                        />
                }
            },
          
            My: {
                screen: My,
                navigationOptions: {
                    tabBarLabel: "我的",
                    tabBarIcon: ({tintColor, focused}) =>
                        <FontAwesome5
                            name={'user-circle'}
                            size={20}
                            color={tintColor}
                        />
                }
            },
        },
        {
            tabBarOptions: {
                activeTintColor: ACTIVATE_COLOR,
                labelStyle: {
                    fontSize: 13,
                },
            }
        }
    )
    

    发现很有可能是因为Home进行了WithNoNet组件包裹,导致navigation组件传递不过去了,怎么办呢?
    先google一把,然并卵,没找到答案,只能自己啃了。。。
    首先我觉得应该在screen参数上下功夫,于是去react-navigation官网看createStackNavigation的 screen相关的东西,然而除了已知的能传一个组件(就像现在的做法)作为参数,也没有发现更多。
    我看代码中 tabBarIcon可以是一个函数返回一个组件, 猜猜screen行不行呢?

    tabBarIcon: ({tintColor, focused}) =>
                        <FontAwesome5
                            name={'user-circle'}
                            size={20}
                            color={tintColor}
                        />
    

    于是试了一把,改为

    Home: {
                screen: ({navigation}) => <Home navigation={navigation}/>,
                navigationOptions: {
                    tabBarLabel: "首页",
                    tabBarIcon: ({tintColor, focused}) =>
                        <Ionicons
                            name={'md-home'}
                            size={20}
                            color={tintColor}
                        />
                }
            },
    

    然后我在WithNoNet中componentDidMount中打印this.props.navigation, 发现真打印出来了,那么这个问题就好办了,直接将navigation传递给子组件就行了。将WithNoNet改为如下

    //WithNoNet高阶组件
    import React from 'react'
    import NetInfo from "@react-native-community/netinfo";
    import NoNetwork from '../common/NoNetwork'
    
    export default WrappedComponent => {
        return class extends React.Component {
            constructor(props) {
                super(props)
                this.state = {
                    isConnected: false,
                    hasCheckedNetwork: false, //为了解决执行网络检查时,NoNetwork页面一闪而过的现象
                }
            }
    
            async componentDidMount(): void {
                await this.checkNetwork()
            }
    
            checkNetwork = async () => {
                try {
                    let netInfo = await NetInfo.fetch()
                    if (netInfo.isConnected) {
                        this.setState({
                            isConnected: true,
                            hasCheckedNetwork: true,
                        })
                    } else {
                        this.setState({
                            hasCheckedNetwork: true,
                        })
                    }
                } catch (e) {
                    console.log(e)
                }
            }
    
            render() {
                const {isConnected, hasCheckedNetwork} = this.state
                const {navigation} = this.props  //获取navigation并传递给WrappedComponent
    
                if (isConnected) {
                    return <WrappedComponent navigation={navigation}/>
                } else {
                    if (hasCheckedNetwork) {
                        return <NoNetwork checkNetwork={this.checkNetwork}/>
                    } else {
                        return null
                    }
                }
            }
        }
    }
    

    然后在Alert,my页面都引入WithNoNet组件,导出是进行包裹, 再将底部导航的所有screen配置为如下:

    const bottomNavigator = createBottomTabNavigator({
            Home: {
                screen: ({navigation}) => <Home navigation={navigation}/>,
                navigationOptions: {
                    tabBarLabel: "首页",
                    tabBarIcon: ({tintColor, focused}) =>
                        <Ionicons
                            name={'md-home'}
                            size={20}
                            color={tintColor}
                        />
                }
            },
            Alert: {
                screen: ({navigation}) => <Alert navigation={navigation}/>,
                navigationOptions: {
                    tabBarLabel: "告警",
                    tabBarIcon: ({tintColor, focused}) =>
                        <MaterialCommunityIcons
                            name={'alert-decagram'}
                            size={20}
                            color={tintColor}
                        />
                }
            },
          
            My: {
                screen: ({navigation}) => <My navigation={navigation}/>,
                navigationOptions: {
                    tabBarLabel: "我的",
                    tabBarIcon: ({tintColor, focused}) =>
                        <FontAwesome5
                            name={'user-circle'}
                            size={20}
                            color={tintColor}
                        />
                }
            },
        },
        {
            tabBarOptions: {
                activeTintColor: ACTIVATE_COLOR,
                labelStyle: {
                    fontSize: 13,
                },
            }
        }
    )
    

    同时附上NoNetwork页面的代码

    import React from 'react'
    import {TouchableOpacity, Text, StyleSheet, Dimensions, Image} from 'react-native'
    
    const errorImg = require('../res/icons/color/noNetwork.png')
    const {height} = Dimensions.get('window')
    export default class NoNetwork extends React.PureComponent {
        render() {
            return <TouchableOpacity
                style={styles.wrapper}
                onPress={this.props.checkNetwork}
            >
                <Image
                    source={errorImg}
                    style={styles.img}
                />
                <Text>网络跑丢啦!!</Text>
                <Text>轻触重试</Text>
            </TouchableOpacity>
        }
    }
    const styles = StyleSheet.create({
        wrapper: {
            height: height - 80,
            flex: 1,
            justifyContent: 'center',
            alignItems: 'center',
        },
        img: {
            width: 40,
            height: 40,
        },
    })
    

    大功告成了,哈哈哈,解决了这个问题,让我对react的高阶组件理解更深刻了,也更喜欢高阶组件了。
    同时希望能帮助到有相同问题的同学!!!

    相关文章

      网友评论

          本文标题:react-navigation的screen参数使用高阶组件时

          本文链接:https://www.haomeiwen.com/subject/cskbzqtx.html