Rob
Rob

Reputation: 3459

Dynamically Create Ref in Constructor

I want to make my child components have a ref, but I don't want the user to have to specify the ref when they create the component. So say I have something like this:

<Parent>
    <Child />
    <Child />
</Parent>

And I want the parent to be able to access the Child components' state. The easy way is to add a ref to each component, but I would like this to the be something that is done during the constructor() function to abstract this away from the end user, as I'd like these components to be generalized.

Is there a clean way to make it so the parent can access the state of the child components, such as when the Child is created you have something like:

class Child extends Component {
    constructor(){
        super();
        this.state = {'abc': 123}
        this.ref=Math.random();
    }
}

So that inside of the Parent class I can do something like:

 class Parent extends Component {
     componentWillMount(){
         console.log(this.refs);
     }
 }

So I want to be able to declare a set of components like:

class App extends Component {
    render(){
       <Parent> <Child /> <Child /> </Parent>
    }
}

So that I can access each child and it's state as the parent iterates through child components.

Upvotes: 3

Views: 10413

Answers (3)

gazdagergo
gazdagergo

Reputation: 6691

In this approach the Parent component is the one where the children are being used in render. You can manipulate the childrens' "state" by calling a method of them.

Parent.js

class Parent extends Component {
  refs = [];

  componentDidMount() {
    this.refs[0].doSelect();
  }

  setRef = ref => {
    this.refs.push(ref);
  }

  render() {
    return (
      <div>
        <Child ref={this.setRef} />
        <Child ref={this.setRef} />
      </div>
     )
   }
}

Child.js

class Child extends Component {
  state = { isSelected: false }

  doSelect = () => {
    this.setState({ isSelected: true })
  }

  render() {
    return <div className="child" />
  }
}

Upvotes: 3

Itsca
Itsca

Reputation: 394

I needed this exact same thing for a form I was making and this is what I came up with to add the refs to the child components automatically

Given that you have:

<Parent>
    <Child />
    <Child />
</Parent>

You can use this in the Parent component render function to add a ref to every child.

render () {
  let wrappedChildren = [];

  React.Children.map(this.props.children, (child, i)=> {
    if (!child) {
      return;
    }
      let refName = 'child' + i
      wrappedChildren.push(React.cloneElement(child, {
        key: refName,
        ref: refName,
      }
    ));
  }, this);
  return (
    <View>
        {wrappedChildren}
    </View>
  )
}

This will add a ref to every Child consisting of a string plus the index of the current map and then render them in the same order, giving you the equivalent of doing this:

<Parent>
    <Child ref="child0" />
    <Child ref="child1" />
</Parent>

I know that string refs are deprecated but i was trying to keep it simple you can also use refs as callbacks by changing:

ref: refName

to:

ref: (c) => { this[refName] = c }

And then used it in the parent component as this.child0 for example.

Hope it helps.

Upvotes: 5

Ved
Ved

Reputation: 12093

  1. I want the parent to be able to access the Child components' state.

State is only accessible to component itself. Parent and child component can access only their own state. You can pass value to child state as props but parent can't access child component state itself.

  1. Setting Refs to child component.

    Why do you want this? Ref is for
    Managing focus, text selection, or media playback.
    Triggering imperative animations.
    Integrating with third-party DOM libraries.

    Please visit this link to understand the uses of Ref.
    https://facebook.github.io/react/docs/refs-and-the-dom.html

Anyway, components can have their own refs, and you can set ref to child components,

render: function() {
    return (
      <div>
        <Child ref="child" />
        <div><button onClick={this.handleClick}>ACCESS REF</button></div>
      </div>
    );
  },

handleClick: function() {
    alert(this.refs.child.refs.input.getDOMNode().value);
  }
});

let Child = React.createClass({
  render: function() {
    return <input ref="input" />;
  }
});

I will suggest , you should either pass callbacks into children as props to do your stuff.:

Upvotes: 2

Related Questions