Search code examples
javascriptformsreactjsreactjs-flux

ReactJS: access child method


I'm trying to make a simple form system for a project I'm working on, and the idea is simple. I have a FormStore where forms can be registered, components can be registered to those forms and all the data can be retrieved.

I want to make this as automated as possible, so when the <Form> element is mounted, it should search any children for a isFormItem property, and automatically register that input field with some callback to get the values, defined within the input element.

The issue I'm facing is that I don't know how I would access the child component from the higher level component. I don't think there's a way to use refs, since those have to be manually assigned, and that's what I'm trying to avoid.

This is the relevant snippet from my Form.react.jsx. This is where I try to attach each Input field to the form.

  componentDidMount() {
    const name = this.props.name ||
      'form_' + Math.floor(Math.random() * 100000000);
    FormStore.registerForm(name);
    React.Children.forEach(this.props.children, (child) => {
      if (selectn('props.isFormItem', child)) {
        // I need to call child.attachToForm somehow...
      }
    });
  };

And these are the relevant parts of Input.react.jsx.

  constructor(props) {
    this.attachToForm = this.attachToForm.bind(this);
  }

  attachToForm(formName) {
    const name = this.props.name ||
      'formItem_' + Math.floor(Math.random() * 100000000);
    FormStore.attachToForm(formName, name, () => this.state.value);
  }

I have tried accessing the child component method like this:

  React.Children.forEach(this.props.children, (child) => {
    if (selectn('props.isFormItem', child)) {
      child.type.prototype.attachToForm =
        child.type.prototype.attachToForm.bind(child);
      child.type.prototype.attachToForm(name);
    }
  });

But calling the prototype doesn't bind the actual object instance anywhere, so that ended up in having a whole bunch of binds everywhere, and in the end it didn't evne work in addition to being terribly messy all around.

Is there some way of doing this? I am just not seeing it... Any help would be appreciated.


Solution

  • You can pass the formName as a prop to each Input component and then attach each item in it‘s own componentDidMount method instead.

    Also, the getDefaultProps method seems handy in your case.

    Something like:

    getDefaultProps() {
      return {
        name: 'form_' + Math.floor(Math.random() * 100000000)
      }
    }
    render() {
       return <Input formName={this.props.name} />
    }
    

    And then in the Input component:

    getDefaultProps() {
      return {
        name: 'formItem_' + Math.floor(Math.random() * 100000000)
      }
    }
    componentDidMount() {
      FormStore.attachToForm(this.props.formName, this.props.name, () => this.state.value)
    }