Andrew Ives
Andrew Ives

Reputation: 487

React Native - Call Function of child from NavigatorIOS

I am trying to call a child function from the right button on the parent navigator.

A basic code example of what I need is as follows:

Main.js

<NavigatorIOS
    style={styles.container}
    initialRoute={{
        title: 'Test',
        component: Test,
        rightButtonTitle: 'Change String',
        onRightButtonPress: () => ***I Want to call miscFunction from here*** ,
        passProps: {
            FBId: this.state.fbidProp,
            favsPage: true
        }
    }}/>

Test.js

class Test extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            variable: 'some string'
        };
    }
    miscFunction(){
        this.setState({
            variable: 'new string'
        };
    }
    render(){
        return(
            <Text> {variable} </Text>
        )
    }
}

Upvotes: 1

Views: 5121

Answers (3)

backslash112
backslash112

Reputation: 2230

You can use the flux, here is a demo: https://github.com/backslash112/react-native_flux_demo

Upvotes: 0

Suran
Suran

Reputation: 260

A. In initial component

this.props.navigator.push({
    title: 'title',
    component: MyComponent,
    rightButtonTitle: 'rightButton',
    passProps: {
        ref: (component) => {this.pushedComponent = component},
    },
    onRightButtonPress: () => {
        // call func
        this.pushedComponent && this.pushedComponent.myFunc();
    },
});

B. In pushed component
replace onRightButtonPress func in pushed component.

componentDidMount: function() {
    // get current route
    var route = this.props.navigator.navigationContext.currentRoute;
    // update onRightButtonPress func
    route.onRightButtonPress =  () => {
        // call func in pushed component
        this.myFunc();
    };
    // component will not rerender
    this.props.navigator.replace(route);
},

Upvotes: 0

Colin Ramsay
Colin Ramsay

Reputation: 16476

This is covered in the following github issue:

https://github.com/facebook/react-native/issues/31

Eric Vicenti comments to describe how Facebook solves this internally:

Currently the best way to do that is to create an EventEmitter in the owner of the NavigatorIOS, then you can pass it down to children using route.passProps. The child can mix in Subscribable.Mixin and then in componentDidMount, you can

this.addListenerOn(this.props.events, 'myRightBtnEvent', this._handleRightBtnPress);

It is clear that this API needs improvement. We are actively working the routing API in Relay, and hopefully react-router, but we want NavigatorIOS to be usable independently. Maybe we should add an event emitter inside the navigator object, so child components can subscribe to various navigator activity:

this.addListenerOn(this.props.navigator.events, 'rightButtonPress', this._handleRightBtnPress);

Here's how this looks in a practical example:

'use strict';

var React = require('react-native');
var EventEmitter = require('EventEmitter');
var Subscribable = require('Subscribable');

var {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    NavigatorIOS
} = React;

First we pull in all of our requirements including the EventEmitter and Subscribable.

var App = React.createClass({
    componentWillMount: function() {
        this.eventEmitter = new EventEmitter();
    },

    onRightButtonPress: function() {
        this.eventEmitter.emit('myRightBtnEvent', { someArg: 'argValue' });
    },

    render: function() {
        return <NavigatorIOS
                style={styles.container}
                initialRoute={{
                    title: 'Test',
                    component: Test,
                    rightButtonTitle: 'Change String',
                    onRightButtonPress: this.onRightButtonPress,
                    passProps: {
                        events: this.eventEmitter
                    }
                }}/>
    }
});

In our main top-level component, we create a new EventEmitter (in componentWillMount) to be available across the component, and then use passProps to pass it down to the Test component we specify for the navigator.

We also define a handler for the right button press, which emits a myRightBtnEvent with some dummy arguments when that button is pressed. Now, in the Test component:

var Test = React.createClass({
    mixins: [Subscribable.Mixin],

    getInitialState: function() {
        return {
            variable: 'original string'
        };
    },

    componentDidMount: function() {
        this.addListenerOn(this.props.events, 'myRightBtnEvent', this.miscFunction);
    },

    miscFunction: function(args){
        this.setState({
            variable: args.someArg
        });
    },
    render: function(){
        return(
            <View style={styles.scene}><Text>{this.state.variable}</Text></View>
        )
    }
});

We add the Subscribable mixin, and the only other thing we need to do is listen out for the myRightBtnEvent being fired from the App component and hook miscFunction up to it. The miscFunction will be passed the dummy arguments from the App press handler so we can use those to set state or perform other actions.

You can see a working version of this on RNPlay:

https://rnplay.org/apps/H5mMNQ

Upvotes: 4

Related Questions