繁体   English   中英

从react-native的Navigator按钮调用组件函数

[英]Call a component function from react-native's Navigator buttons

我看到这个问题在非常不同的环境中四处徘徊,没有任何直接解决方案。 有人问我,我正在向我展示一些示例,说明如何使用导航执行我想要的代码,因为其中大多数已经采用了第三方解决方案,或者使用<View>

为了使问题有一定的背景,这是我们要完成的工作:

class Example extends Component {
    componentRouteMapper() {
        switch(route.id) {
            case 'home' return <Home navigator={navigator} { ...this.props } />
        }
    }

    navigationBarRouteMapper = {
        LeftButton: (route, navigator, index, navState) => {
            return <TouchableOpacity onPress={this.onLeftPressed.bind(this)}>
                <Text>Left Button</Text>
            </TouchableOpacity>
        },
        RightButton: (route, navigator, index, navState) => {
            // ... Same as left
        },
        Title: (route, navigator, index, navState) => {
            return <Text>{route.id}</Text>
        }
    }

    render() {
        return <Navigator initialRoute={{id: 'home'}}
            renderScene={this.componentRouteMapper}
            navigationBar={
                <Navigator.NavigationBar { ...this.props } 
                    routeMapper={this.navigationBarRouteMapper}/>
            }

    }
}

自然,您将希望能够从已渲染组件的上下文中处理导航栏逻辑。 在这种情况下: <Home /> ,我们将希望能够从该组件活动实例共享状态/属性。 即使您在home组件中声明了onLeftPressed() ,执行上述代码也会导致错误,如下所示:

export default class Home extends Component {

    onLeftPressed() {
        console.log('Pressed left button')
    }

    render() {
        return <View>
            <Text>Home Component</Text>
        </View>
    }
}

所以眼前的问题是...如何从navigationBarRouteMapper调用onRightPressed()

它实际上非常简单,虽然还有其他方法,例如在Router中注入组件,但是考虑到我的应用程序使用Redux ,将connect()方法传递给Route对象有点麻烦。

因此,我想出了一个简单而有效的解决方案,无论您使用什么来管理状态,该解决方案都可以工作。 注射剂 没错,只需将您需要调用的内容直接插入到navigation对象中即可。

可以通过this.props.navigator从组件内部访问navigation对象,这对我们来说是幸运的,Javascript对象是可变的,并通过引用自然传递。 这意味着我们对导航器对象所做的任何更改都将反映在注入到navigationBarRouteMapper

因此,首先让我们更改一些代码:

navigationBarRouteMapper = {
    LeftButton: (route, navigator, index, navState) => {
        return <TouchableOpacity onPress={navigator.__onLeftNavButtonPressed}>
            <Text>Left Button</Text>
        </TouchableOpacity>
    },
    RightButton: (route, navigator, index, navState) => {
        // ... Same as left
    },
    Title: (route, navigator, index, navState) => {
        return <Text>{navigator.navTitle || route.id}</Text>
    }
}

您会注意到,我已经将onPress值从{this.onLeftPressed.bind(this)}更改为{navigator.__onLeftNavButtonPressed}现在显然可以更改正在使用的名称,但是我喜欢双下划线前缀。

您还将看到我将标题更改为返回{navigator.navTitle || route.id} {navigator.navTitle || route.id}而不是route.id ,这意味着它将使用navigator中的navTitle属性(如果可用),如果不可用,则将使用堆栈顶部的路由ID。

现在,让我们对<Home />组件进行更改,以便我们的navigationBarRouteMapper执行我们想要的代码。

class Home extends Component {

    constructor(props) {
        super(props)
        var navigator = this.props.navigator
        navigator.navTitle = 'Home'
        navigator.__onLeftNavButtonPressed = this.onLeftPressed.bind(this)
    }

    onLeftPressed() {
        console.log('Pressed left button')
    }

    render() {
        return <View>
            <Text>Home Component</Text>
        </View>
    }
}

如您所见,唯一更改的是添加了一个constructor(...) ,该函数将函数和标题注入了navigator对象。 现在,如果单击左按钮,将在Home组件内部执行onLeftPressed函数。

但是,您可以通过创建自定义组件子类来对其进行抽象。 例如:

import React, { Component } from 'react'

export default class NavComponent extends Component {
    constructor(props) {
        super(props)
        console.warn('Constructed')
        var navigator = this.props.navigator
        navigator.navbarTitle = this.__onNavTitleRequested();
        navigator.__onLeftNavButtonPressed = this.__onLeftNavButtonPressed.bind(this)
        navigator.__onRightNavButtonPressed = this.__onRightNavButtonPressed.bind(this)
    }


    __onLeftNavButtonPressed() {

    }

    __onRightNavButtonPressed() {

    }

}

这样可以确保始终设置左右导航按钮功能,即使它们什么也不做,也可以防止由于尝试调用未定义的功能而导致应用程序错误。 您还将注意到,存在对__onNavTitleRequested的引用,但是对此没有默认功能,这是为了确保子类必须定义__onNavTitleRequested ,否则应用程序将无法正常运行。 这样可以防止您在标题栏中使用标题不正确的组件,因为如果您未设置标题,它将始终使用最后设置的标题。

一个实现此目的的示例如下:

import React from 'react'
import NavComponent from './path/to/nav-component.js'

export default class Home extends NavComponent {
    __onNavTitleRequested() {
        return 'Home'
    }

    __onLeftNavButtonPressed() {
        console.log('Left navigation button pressed')
    }

    __onRightNavButtonPressed() {
        console.log('Right navigation button pressed')
    }

    render() {
        return <View>
            <Text> Home Component </Text>
        </View>
    }
}

这会将一个事件注册到“向左/向右”按钮并设置Home组件的标题。 您也可以向Navigator对象中注入更多信息,例如,如果您想让__hasNavigationBar函数返回true或false以隐藏整个导航栏,或者__rightNavButton函数返回要在右侧按钮上显示的组件,只是一个TouchableOpacity按钮。

如前所述,还有其他方法可以做到这一点,但是考虑到我在应用程序中使用Redux似乎是最简单的方法,而我对此没有任何问题。

一种替代方法是使用事件总线,例如react-native-event-listeners。 我能够通过这种方式解决问题,并且比Hobbyist的解决方案简单得多。

基本上,它的工作方式是按钮将消息发送给接收方,然后接收方(在组件的上下文中)将执行必要的操作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM