MKeller
MKeller

Reputation: 377

How to access child component functions via refs

The docs for React state that component functions can be accessed by a parent component via refs. See: https://facebook.github.io/react/tips/expose-component-functions.html

I am attempting to use this in my application but run into an "undefined is not a function" error when the child function is called. I'm wondering if this has anything to do with using the ES6 format for React classes because I don't see any other differences between my code and the docs.

I have a Dialog component that looks like the following pseudocode. The Dialog has a "Save" button that calls save(), which needs to call the save() function in the child Content component. The Content component collects information from child form fields and performs the save.

class MyDialog extends React.Component {
  save() {
    this.refs.content.save();                    <-- save() is undefined
  }

  render() {
    return (
      <Dialog action={this.save.bind(this)}>
        <Content ref="content"/>
      </Dialog>);
   }
}

class Content extends React.Component {
  save() {
    // Get values from child fields
    // and save the content
  }
}

I could instead pass a prop (saveOnNextUpdate) down to Content and then execute save whenever it is true, but I would rather figure out how to get the method detailed in the React doc above to work.

Any ideas on how to get the doc approach to work or access the child component function in a different way?

Upvotes: 34

Views: 21903

Answers (3)

Divyanshu Maithani
Divyanshu Maithani

Reputation: 14976

An alternative way to do this would be to use some other prop name (other than ref). I've found that this also works well if you're using a library like styled-components or emotion For example in a connected MyComponent:

<MyComponent
  ...
  innerRef={(node) => { this.myRef = node; }}
/>

Upvotes: 2

Arne H. Bitubekk
Arne H. Bitubekk

Reputation: 3033

Redux connect accepts an option parametre as the forth parameter. In this option parameter you can set the flag withRef to true. Then you can access functions to refs by using getWrappedInstance(). Like this:

class MyDialog extends React.Component {
  save() {
    this.refs.content.getWrappedInstance().save();
  }

  render() {
    return (
      <Dialog action={this.save.bind(this)}>
        <Content ref="content"/>
      </Dialog>);
   }
}

class Content extends React.Component {
  save() { ... }
}

function mapStateToProps(state) { ... }

module.exports = connect(mapStateToProps, null, null, { withRef: true })(Content);

Read more about it here: https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options

Worth reading this article about use of refs and consider if there's better approaches: https://facebook.github.io/react/docs/refs-and-the-dom.html#dont-overuse-refs

Upvotes: 51

MKeller
MKeller

Reputation: 377

As it turns out, m90 was right -- this was a different issue entirely. I'm posting the solution in case someone runs into the same problem in the future.

My application is built with Redux, and the problem stems from using the react-redux connect function to connect a component to the store/global state. For some reason, exporting a component and connecting it to the store makes it impossible to access the functions inside of it. In order to get around this, I had to remove all use of global state from Content so that I could export it as a "dumb" component.

To be more clear, Content.js looked like this:

var connect = require('react-redux').connect;

class Content extends React.Component {
  save() {
    // Get values from child fields
    // and save the content

    // Use of this.props.stateObject
  }
}

function mapStateToProps(state) {
  const {
    stateObject
  } = state;

  return {
    stateObject
  };
}

module.exports = connect(mapStateToProps)(Content);

Removing the use of global state (and therefore the use of connect and mapStateToProps allowed me to export the component using:

module.exports = Content;

Accessing this.refs.content.save() magically worked after doing this.

Upvotes: -2

Related Questions