React: Access to `this.props.children` functions

Created on 30 Jan 2015  ·  29Comments  ·  Source: facebook/react

The following is a snippet to demonstrate the use case I am trying to achieve.

var Parent = React.createClass({
  componentDidMount: function() {
    React.Children.forEach(this.props.children, function(child) {
      console.log('test' in child);
    });
  },

  render: function() {
    return <div>{this.props.children}</div>;
  }
});

var Child = React.createClass({
  test: function() {
    console.log('this is a test');
  },

  render: function() {
    return <span>Child</span>;
  }
});

React.render(<Parent><Child/></Parent>, document.body);

Like you'd know, while iterating through this.props.children I am unable to get a reference to the actual component, or at least not able to figure out how so that I could access the child component's API.

Is there anyway I could do this !? If not, why !?

Edit: I am aware that I can add a ref to the child component and then use that to do this, but my goal is that the parent should not care about what it's children are or restrict / limit that in any way.

Most helpful comment

I've just faced the same issue while building a custom Form component which is supposed to automatically call .validate() on all of its Inputs on form submission.
Seems there's no right solution for this.

I'll try to hack it with passing refs from the page component to the Form component via a refs prop.

Edit: seems that this concept won't work too because Inputs can be anywhere deep inside the Form (in a <div/>, etc).
Seems that the way to go is to explicitly pass the inputs property (a function returning an array of React components) to the Form so that it can call .validate() on each of them on form submission.

Edit: done, here's the resulting code:

import React, { Component, PropTypes } from 'react'

export default class Form extends Component
{
    static propTypes =
    {
        action    : PropTypes.func.isRequired,
        inputs    : PropTypes.func,
        className : PropTypes.string,
        style     : PropTypes.object
    }

    render()
    {
        const { className } = this.props

        const markup = 
        (
            <form className={className} style={this.props.style} onSubmit={::this.submit}>
                {this.props.children}
            </form>
        )

        return markup
    }

    submit(event)
    {
        event.preventDefault()

        const { inputs } = this.props

        if (inputs)
        {
            for (let input of inputs())
            {
                if (!input.validate())
                {
                    return input.focus({ preserve_validation: true })
                }
            }
        }

        this.props.action()
    }
}
            <Form 
                ref="form" 
                className="authentication-form"
                action={this.sign_in}
                inputs={() => [this.refs.email, this.refs.password]}>

                <Text_input
                    ref="email"
                    value={this.state.email}
                    validate={this.validate_email_on_sign_in}
                    on_change={email => this.setState({ email })}/>

                <Text_input
                    ref="password"
                    value={this.state.password}
                    on_change={password => this.setState({ password })}
                    validate={this.validate_password_on_sign_in}/>

All 29 comments

this.props.children contains elements, not component instances. In this case child in your forEach handler will get the result of <Child/>, or without JSX React.createElement(Child, null).

There's no way to do what you want from within Parent (well there might be with React.Children.map and cloneWithProps and refs, but I don't recommend it).

Try restructuring your code so that you don't need to do this.

I am actually already using cloneWithProps since I also need to pass on some props to my children. React.Children.map too however does not in any way give me access to this.

Yeah, it appears there's no way to do this. I suggest finding a way to make this not a requirement, because it's multiple levels of confusing (a component which invokes internal methods of its children without even the hint of a ref at the construction of the child isn't something I'd want to happen ever).

That is simply not how React is intended to work, it should not work that way and it cannot work that way.

You create elements which you pass to React which basically becomes "I want the view hierarchy to look like this". React then takes that request and creates/reuses/destroys components according to its reconciliation algorithm. If you need access to instances before that you're approaching the problem in the wrong way. React components should _not_ be compared with OOP or more classical object-based view hierarchies. Components are in a sense thin and should not have complex logic, such logic should exist outside of React and be communicated and propagates through the view hierarchy via props.

A parent accessing a child is generally considered an anti-pattern I would say.

@syranide Well perhaps I should share the exact use case that I am trying to solve here and that might throw some light on my motivations and perhaps add a little more sense to all this and why it might be a great idea. I will be releasing it as open source soon so I don't mind sharing the idea here.

I have built a custom Form & Field component, the form component is simple enough besides that it wants to hand down certain props to all it's fields, but doesn't want to care about the details of their markup and such

The Field component is an abstraction over form components out of which I can easily make further components such as InputField, EmailField, SelectField, etc. for using with the Form, the idea is they should all have a consistent api. This abstracts displaying labels (or not), the form components themselves, error messages, etc all based on props.

A basic form would look something like this :

<Form
  i18n='form'
  onSubmit={this.handleSubmit}>
  <InputField name='first_name'/>
  <InputField name='last_name'/>
  <SelectField name='gender'>
    {genderOptions}
  </SelectField>
  <NestedField name='address_attributes'>
    <InputField name='city'/>
    <InputField name='country'/>
    <TextAreaField name='billing_address'/>
  </NestedField>
</Form>

I think that looks really nice & simple!

Within the handleSubmit what I want is to be able to loop through the children, call their API 'value()' and get the values of the fields. The reason this becomes important is important especially because I could have complex nesting of fields with things like NestedFieldand there's also GroupField which just groups fields together for more layout flexibility. From the perspective of Form, I don't care what fields I have as my children, nor their order and such, I really just care about what they return as their value() and then build & pass on the entire form data hash to the parent of Form.

At the moment, what I have done is gone ahead and used form2js which allows me to build the hash simply from the form markup and it is a decent work around, however if the fields themselves were responsible for declaring what their values should be, things would become a lot more deterministic, I believe.

I think this structure & concept works quite well and feels quite intuitive yet simple, however do share your thoughts on the same if you have some other ideas.

Here's a possible implementation. The React way is to communicate via props, passind data downward and using callbacks for going back up. One way to use that paradigm and still have the nice-looking layout would be:

var Form = React.createClass({
    // ....
    _onChange: function (name, value) {
       var up = {}
       up[name] = value
       this.setState(up)
    },
    render: function () {
        this.props.children.forEach((child) => {
            child.props.onChange = this._onChange.bind(null, child.props.name));
            child.props.value = this.state[child.props.name]
        }
        return <div>{this.props.children}</div>
    }
})

Basically, the form automatically gives all its children an onChange callback that they can call when their state needs to be updated, along with their current value. This way, the Form owns all of the state, and the children can just be stateless form elements.

Does that make sense?

n.b. I haven' t tried this, and it might end up being _too much magic_, but I think it would work out.

So in the child too, i'll handle the onChange and pass it on by calling this.props.onChange(value) !? I guess that might actually work.

Yup. So the children shouldn't own any state, then the form component is always the "single source of truth"

That sounds good, thanks.

Did you find an alternative that works for you? Can we close this out?

Yea the work around proposed by @jaredly is quite decent. Going to close this one out.

There's no way to do what you want from within Parent (well there might be with React.Children.map and cloneWithProps and refs, but I don't recommend it).

Is this the only option? Given that https://facebook.github.io/react/docs/more-about-refs.html says

However, there are a few cases where it still might be necessary or beneficial.

We seem to agree that there are cases when it is useful. Combine this with https://facebook.github.io/react/docs/transferring-props.html

It's a common pattern in React to wrap a component in an abstraction.

and you have a problem.

I absolutely agree that these are edge cases and usually props/state are all you need. But given the example from https://facebook.github.io/react/docs/more-about-refs.html it's currently not possible to create an abstract InputFocuser component which focuses its child.

My use case is this: I'm doing scroll-based animations and you simply can't use props because you will never even get close to 60fps. That's why all my components agreed on an api and implement a handleScrollChange method. Everything is working really nice at the moment (you can see the scrolling in action here https://read.grandma.io/the-grandma-story/). But one of my components needs a refactoring and I would love to drop an abstract ScrollHandler component into the mix which cleans my current top level component. But I can't, because it's not possible to call handleScrollChange on this.props.children. I'm currently looping over this.refs from my top level component.

Any ideas?

I, like OP, too was under the impression that this.props.children contained instances of components. The name seems very misleading if this is not the intended behavior.

Were there any docs that were unclear on this point? We can improve them.

I've just faced the same issue while building a custom Form component which is supposed to automatically call .validate() on all of its Inputs on form submission.
Seems there's no right solution for this.

I'll try to hack it with passing refs from the page component to the Form component via a refs prop.

Edit: seems that this concept won't work too because Inputs can be anywhere deep inside the Form (in a <div/>, etc).
Seems that the way to go is to explicitly pass the inputs property (a function returning an array of React components) to the Form so that it can call .validate() on each of them on form submission.

Edit: done, here's the resulting code:

import React, { Component, PropTypes } from 'react'

export default class Form extends Component
{
    static propTypes =
    {
        action    : PropTypes.func.isRequired,
        inputs    : PropTypes.func,
        className : PropTypes.string,
        style     : PropTypes.object
    }

    render()
    {
        const { className } = this.props

        const markup = 
        (
            <form className={className} style={this.props.style} onSubmit={::this.submit}>
                {this.props.children}
            </form>
        )

        return markup
    }

    submit(event)
    {
        event.preventDefault()

        const { inputs } = this.props

        if (inputs)
        {
            for (let input of inputs())
            {
                if (!input.validate())
                {
                    return input.focus({ preserve_validation: true })
                }
            }
        }

        this.props.action()
    }
}
            <Form 
                ref="form" 
                className="authentication-form"
                action={this.sign_in}
                inputs={() => [this.refs.email, this.refs.password]}>

                <Text_input
                    ref="email"
                    value={this.state.email}
                    validate={this.validate_email_on_sign_in}
                    on_change={email => this.setState({ email })}/>

                <Text_input
                    ref="password"
                    value={this.state.password}
                    on_change={password => this.setState({ password })}
                    validate={this.validate_password_on_sign_in}/>

@dhruvasagar
Did you use cloneWithProps replace all Input components in render method?
How did you handle dynamic Input, which get rendered when yes radio checked, and get destroyed when another no radio checked.

I want to do something like iterate all input/radio/checkbox... when submit a form.
Does there has any way to do this?

For basic component, Container has access to all its children is a very important function, React can't do this?

I have spent lots of time and got disappointed...

@sulinixl I didn't try to handle dynamic inputs case. Overall I was disappointed too, Forms are one of the only hard parts of React right now. I tried to build an abstraction on top of it like I described above to try and abstract underlying implementation details (using bootstrap or any other framework). It worked in that specific scenario but always felt too hacky, hence I did not open source it / improve upon it.

I have made a module named react-node-finder to resolve my problem, I hope it will resolve all your problem~

Use react-node-finder, I can just finder.findChildren(form, Input) to search all Input children.

https://github.com/sulinixl/react-node-finder

How can this be?

Isn't there a simple way to access the instances of children other than manually passing refs to its parent?

Edit: @halt-hammerzeit can you share the code where you create the array of inputs? Is there a dynamic way to do that?

@PierBover Here's how the inputs array function is coded

    render_sign_in_form()
    {
        return (
            <Form 
                ref="form" 
                className="authentication-form"
                inputs={() => [this.refs.email, this.refs.password]}>

                <Text_input
                    ref="email"
                    value={this.state.email}
                    validate={this.validate_email_on_sign_in}
                    on_change={email => this.setState({ email })}/>

                <Text_input
                    ref="password"
                    value={this.state.password}
                    validate={this.validate_password_on_sign_in}
                    on_change={password => this.setState({ password })}/>

Thanks @halt-hammerzeit

So there isn't a dynamic way to do that...

Also, I thought string refs were being deprecated?

@PierBover Well, it turns out they likely are

Although string refs are not deprecated, they are considered legacy, and will likely be deprecated at some point in the future. Callback refs are preferred.

https://facebook.github.io/react/docs/more-about-refs.html#the-ref-string-attribute

Then it could be rewritten as follows:

   <Form
      inputs={() => object_to_array_of_values(this.form_inputs)}>

      <TextInput
         ref={input => this.form_inputs[id] = input}/>

Thanks a lot @halt-hammerzeit

I've been banging my head against a wall for hours and I finally got access to the inputs from the form component and the solution turned out to be a lot simpler than I expected.

export default class SchoolForm extends React.Component {
    constructor(props){
        super(props)
        this.inputs = {}
    }

    render(){
        return(
            <Form inputs={this.inputs}>
                <Input ref={(c) => this.inputs.name = c} name="name" placeholder="Nombre" required/>
            </Form>
        )
    }
}

@PierBover

Is your method works?

In my understand, when React mount Form, this.inputs may be empty. because the callback method of ref could be invoked even more later.

You could try this module react-node-finder.
Use this module, you could just call finder.findChildren(this, Input) in Form class to search all Input children.

Such as(Just an example, Not executable):

import * as finder from 'react-node-finder';
import Input from './Input';
class Form extends React.Component{
    onSubmit(){
        //  get all real Input children instance, as Array.
        let inputArray = finder.findChildren(this, Input);  
    }
    render(...);
}

@sulinixl yes it works because I only access this.props.inputs in Form once the user clicks on the submit button.

Stumbled upon this as I was also making a Form library with _encapsulated_ Fields similar to @dhruvasagar. The cloneWithProps ( cloneElement now) method did the trick for me, whereby I transparently add a ref to each child of the Form element:

onSubmit = () => {
  if (this.fieldRefs.filter((ref) => !ref.isValid()).length !== 0) { return }
  return Object.assign(...this.fieldRefs.map((ref) => ref.export()))
}
render () {
  return <form onSubmit={this.onSubmit}>
    {this.props.children.map((child, index) =>
      React.cloneElement(child, {ref: (ref) => { this.fieldRefs[index] = ref }})
    )}
  </form>

This method only works for fields one-level deep, so you might want to use @halt-hammerzeit's method for deeper hierarchies. Alternatively, for deeper hierarchies, one could also compose the fields with an HoC.

@spicyj I would say the docs do not make it clear that the children data structure does not contain references to component instances. It was quite confusing and unintuitive that when I used an array of refs I had access to each component instance, but when using the this.props.children array I did not have access to the component instance. Intuitively, one would think that a ref and a child should have the exact same interface, but they don't, and it was not clear that they're different. Unsure which docs for children to change as it's mentioned in a few different places without much detail ("Children in JSX", React.Children API, etc)

@agilgur5 Ideally you should also preserve an existing ref, if it exists.

render () {
  return this.props.children.map((child, index) =>
    React.cloneElement(child, {ref: (ref) => {
      this.fieldRefs[index] = ref;
      if (child.ref) {
        child.ref(ref);
      }
    }})
  )

It was quite confusing and unintuitive that when I used an array of refs I had access to each component instance, but when using the this.props.children array I did not have access to the component instance

As the docs mention, elements are just descriptions of what you want to see on the screen. React compares elements and creates/updates/destroys instances based on how the elements change. This is described here and then here in more detail. Did you not find it helpful?

Ideally you should also preserve an existing ref, if it exists.

Noted. Thanks!

As the docs mention, elements are just descriptions of what you want to see on the screen. React compares elements and creates/updates/destroys instances based on how the elements change. This is described here and then here in more detail. Did you not find it helpful?

@gaearon It's not the element vs. component instance part that confused me (albeit the difference is really only mentioned once in the docs in the "Note" on Rendering Elements), but rather that the children data structure isn't really ever clearly described (pun on opaque data structure unintended). For instance, here, the docs barely mentioned what children specifically contains. Further, in the section on refs, the docs use the term "child" and "children" while stating "[The child] could be an instance of a React component, or it could be a DOM element". I'd say this grants an incorrect understanding that this.props.children should contain both elements and component instances by use of the term "child".
I think the confusing usage of the words "child" and "children" necessitate an explicit mention that refs and children are different. The ref section is probably the first place a clarification could be made on the ref side, but on the children side, I'm not sure exactly where the difference could be stated.

Side note: I've been using React for ~3 years now, and while I follow the changes closely, every now and then I look at the docs and see that there's multiple differences. The docs are always improving but existing users may not notice when a clarification has been made (e.g. the "Note" referenced above), even though those clarifications may improve their understanding of certain concepts. Perhaps it would be a good idea to have a section to summarize docs changes in the release notes as well?

but rather that the children data structure isn't really ever clearly described (pun on opaque data structure unintended)

I think elements are described here. Do you have thoughts on where we need to add links to that section?

For instance, here, the docs barely mentioned what children specifically contains.

Perhaps we should link to this section from there. Want to send a PR?

Further, in the section on refs, the docs use the term "child" and "children" while stating "[The child] could be an instance of a React component, or it could be a DOM element". I'd say this grants an incorrect understanding that this.props.children should contain both elements and component instances by use of the term "child".

I agree, but can you suggest a better way of phrasing there? It’s a bit tough because “child” means a relationship between components, not an element or instance specifically. Otherwise it’s hard for docs to sound natural. But I’m open to suggestions.

Was this page helpful?
0 / 5 - 0 ratings