Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In React Native, how can I access methods of one component from another component?

Tags:

react-native

I'm trying to access a method of a React Native component from a different component. It is passed through props. Unfortunately, it seems like the components aren't providing their methods publicly. How can I get access to the method?

Have a look at the following, you'll see InsideView has this.props.myModal, which is a ShowMyModal component. However, it doesn't have access to the .openModal() method.

enter image description here

'use strict';

var React = require('react-native');
var {
  AppRegistry,
  ActionSheetIOS,
  StyleSheet,
  Text,
  View,
} = React;

var InsideView = React.createClass({
  makeItOpen: function() {
    debugger;
    this.props.myModal.openModal();
  },

  render: function() {
    return (
      <View>
        <Text onPress={() => this.makeItOpen()}>Click me!</Text>
      </View>
    );
  }
});

var ShowMyModal = React.createClass({
  getInitialState: function() {
    return {
      isModalOpen: false,
    }
  },

  openModal() {
    this.setState({isModalOpen: true});
  },

  closeModal() {
    this.setState({isModalOpen: false});
  },

  render: function() {
    return (
      <Text>isModalOpen = {String(this.state.isModalOpen)}</Text>
    );
  }
});

var AwesomeProject = React.createClass({
  getInitialState: function() {
    return {
      myModal: <ShowMyModal />,
    }
  },

  render: function() {
    return (
      <View style={{padding: 30}}>
        <InsideView myModal={this.state.myModal}/>
        {this.state.myModal}
      </View>
    );
  },
});

AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);
like image 277
Some Guy Avatar asked Aug 13 '15 20:08

Some Guy


2 Answers

Something like this should work:

'use strict';

var React = require('react-native');
var {
  AppRegistry,
  ActionSheetIOS,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} = React;

var InsideView = React.createClass({
  render: function() {
    return (
      <View>
        <TouchableOpacity onPress={() => this.props.openModal()}><Text>Open modal!</Text></TouchableOpacity>
        <TouchableOpacity onPress={() => this.props.closeModal()}><Text>Close modal!</Text></TouchableOpacity>
      </View>
    );
  }
});

var ShowMyModal = React.createClass({
  render: function() {
    return (
      <Text>isModalOpen = {String(this.props.isVisible)}</Text>
    );
  }
});

var SampleApp = React.createClass({
  getInitialState: function() {
    return {
      isModalOpen: false
    }
  },

  _openModal: function() {
    this.setState({
      isModalOpen: true
    });
  },

  _closeModal() {
    this.setState({
      isModalOpen: false
    });
  },

  render: function() {
    return (
      <View style={{padding: 30}}>
        <InsideView openModal={this._openModal} closeModal={this._closeModal}/>
        <ShowMyModal isVisible={this.state.isModalOpen}/>
      </View>
    );
  },
});

AppRegistry.registerComponent('SampleApp', () => SampleApp);
like image 131
Dave Sibiski Avatar answered Oct 21 '22 13:10

Dave Sibiski


I don't think it's a good idea to store the components in state. State should really be used for component's data rather than sub-components. Dave's solution above is good approach but it could be done a bit better as it moves the state of modal to the application (which is not very good to separate concerns). It's good if modal can keep it's own state and know if it's visible or not. Then openModal() and closeModal() can do some extra stuff as needed (as opposed to somehow reacting to change in visibility of ShowModal). You can also avoid those extra _openModal and _closeModal which are boilerplate.

I think it's best to use refs. Refs is standard way to refer to other components. See here for more details about refs https://facebook.github.io/react/docs/more-about-refs.html You can use refs as strings and refer to the component by that strings but it's kind of ugly as introduces global names which contradict the component approach of react. But you can also use callbacks as refs to set your internal components as fields. There is a nice and simple example of this is react's documentation: http://facebook.github.io/react-native/docs/direct-manipulation.html#forward-setnativeprops-to-a-child. I copy it here in case the documentation gets updated:

var MyButton = React.createClass({
  setNativeProps(nativeProps) {
    this._root.setNativeProps(nativeProps);
  },

  render() {
    return (
      <View ref={component => this._root = component} {...this.props}>
        <Text>{this.props.label}</Text>
      </View>
    )
  },
});

What happens here - the view in question has callback ref which sets this._root as the view's backing component. Then in any other place in the component you can use this._root to refer to it.

So in your case it could look like below (note that you need those anonymous arrow functions rather than passing the openModal / closeModal methods because at the time of rendering _modal is not yet set, you can only refer to it later using the anonymous methods).

 // ...
 // InsideView render (same as in Dave's solution) 
  <View>
    <TouchableOpacity onPress={() => this.props.openModal()}><Text>Open modal!</Text></TouchableOpacity>
    <TouchableOpacity onPress={() => this.props.closeModal()}><Text>Close modal!</Text></TouchableOpacity>
  </View>
 // ...
 // Sample App render ...
  <View style={{padding: 30}}>
    <InsideView openModal={ () => this._modal.openModal() } closeModal={ () => this._modal.closeModal() } />
    <ShowMyModal ref={component => this._modal = component} />
  </View>

Then your initial ShowModal implementation can stay as it is - with it's own state and own openModal and showModal functions.

like image 33
Jarek Potiuk Avatar answered Oct 21 '22 13:10

Jarek Potiuk