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

2019-05-23  本文已影响0人  黑哥_2c57

场景:

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的高阶组件理解更深刻了,也更喜欢高阶组件了。
同时希望能帮助到有相同问题的同学!!!

上一篇下一篇

猜你喜欢

热点阅读