Enter Your Email Address to Watch This Lesson

Your link to unlock this lesson will be sent to this email address.

Unlock this lesson and all 1011 of the free egghead.io lessons, plus get React content delivered directly to your inbox!



Existing egghead members will not see this. Sign in.

Use React ref to Get a Reference to Specific Components

4:49 React lesson by

When you are using React components you need to be able to access specific references to individual component instances. This is done by defining a ref. This lesson will introduce us to some of the nuances when using ref.

Get the Code Now
click to level up

egghead.io comment guidelines

Avatar
egghead.io

When you are using React components you need to be able to access specific references to individual components. This is done by defining a ref.

Avatar
Artem

I guess I'll find out this later, but why are the other values on range elements changed when you pulled on of them for the first time?

In reply to egghead.io
Avatar
Roman

In your example each state has initial value of 0, but each slider initial value is 128 (since min=0, max=255 and the thumb is in the middle of the slider).

What is the way to initialize these in sync when using refs? Maybe there's a better approach?

Avatar
Roman

Ok, "Mixins" section already answered the question.

Pass initial value as a prop and use it with defaultValue prop.

In reply to Roman
Avatar
Joel

Are we dipping into the DOM 3x each time a single slider is adjusted? If so, any best practice advice for only querying the DOM element (slider) being adjusted? I can get it to work by binding this and the color to each Slider component, but it seems hacky:
Slider ref="red" update={this.update.bind(this, "red")}

Avatar
Rick

You're right, it should be:
red: React.findDOMNode(this.refs.red.refs.inp).value,
green: React.findDOMNode(this.refs.green.refs.inp).value,
blue: React.findDOMNode(this.refs.blue.refs.inp).value

In reply to Tim
Avatar
Luis

Did anyone got this to work? I am using ES6 right now? I would love to see at least at gist.

In reply to amitgaur
Avatar
Chris

I do not see the value in doing .getDOMNode vs, inp.getDOMNode.

Avatar
León

Am I the only one who see jQuery in this? I mean, "ref" depends on the structure of the HTML. If you change the hierarchy in the view it will fail

Avatar
Joseph

@León - it's not HTML, it's JSX which represents JS functions.

<Slider ref="red" update={this.update} />

is translated to:

React.createElement(Slider, { ref: "red", update: this.update })

In reply to León
Avatar
Mark

I believe you don't need to use findDOMNode at all now. You can just do:

red: this.refs.red.refs.inp.value,
                green: this.refs.green.refs.inp.value,
                blue: this.refs.blue.refs.inp.value
In reply to Rick
Avatar
Ritesh

this stackoverflow answer says to avoid refs. Also the documentation sounds like refs are not ideal. Can this be built easily without using refs? Should refs actually be avoided? Why or why not?

Avatar
Joe

I think this video is out of date and the finished code is different than the code that is below, which took me some time to realize.

Inside the slider class you want to make sure the input element has a ref called "inp". Like this:

class Slider extends React.Component {
render() {
return (



);
}
}

The App itself needs to pull the value like this:

ReactDOM.findDOMNode(this.refs.red.refs.inp).value

The whole update method looks like this:

update(e) {
this.setState({
red: ReactDOM.findDOMNode(this.refs.red.refs.inp).value,
green: ReactDOM.findDOMNode(this.refs.green.refs.inp).value,
blue: ReactDOM.findDOMNode(this.refs.blue.refs.inp).value
});
}

Avatar

Is anyone else getting this error in the update method?
Uncaught ReferenceError: ReactDOM is not defined

I dont seem to be getting this though when calling ReactDOM.render at the bottom of App.js

Refs are a way for us to reference a node, or an instance of a component in our application. To get us started here, I'm creating a component that outputs a single input field. It's going to have an onChange event equal to this.update.bind to this. Right after, we're going to put out a bit of state, it's going to be this.state.a.

App.js

import React from 'react';

class App extends React.Component {
  render(){
    return (
      <div>
        <input
          type="text"
          onChange={this.update.bind(this)}
        /> {this.state.a}
      </div>
    )
  }
}

export default App

We're going to go ahead and setup a constructor, we can create our initial state. We are going to call super() to get our context, and we're going to set this.state equal to an object with a key of a, which is equal to an empty string. I'm going to go ahead and create our update method.

We're going to follow a familiar pattern here, we're just going to take an event off of the <input> field, and we're going to call this.setState or a equal to the event e.target.value.

class App extends React.Component {
  constructor(){
    super();
    this.state = {a: ''}
  }
  update(e){
    this.setState({a: e.target.value})
  }
  render(){ ... }
}

It's going to be the value from our input field. We're going to save that, try it out in the browser, everything's going to work as expected when we type in the <input> field, it updates our state.

Let's go ahead and say we wanted to have two of these guys, and drop in a <hr> just to break these up. On this one, we want it to update a state of b. Now, this isn't going to work with the existing pattern that we have in place, but we're just going to try it, we can see what happens. We're going to update our b with that value, as well.

class App extends React.Component {
  constructor(){
    super();
    this.state = {a: '', b: ''}
  }
  update(e){
    this.setState({
      a: e.target.value,
      b: e.target.value
    })
  }
  render(){ return (
      <div>
        <input
          type="text"
          onChange={this.update.bind(this)}
        /> {this.state.a}
        <hr />
        <input
          type="text"
          onChange={this.update.bind(this)}
        /> {this.state.b}
      </div>
    ) 
  }
}

Now, when we type in the first field, the a field, it's updating both our a and our b state. Likewise, if we type in the b field, it's going to update our a and our b state. That's because we haven't differentiated between these two inputs.

Updates both state values

We can use a ref for that. On this guy, I'm going to say ref="a", and on the second field, I'm going to say ref="b", and that gives us a reference to each of these. I'm going to go ahead and kill off the event in our update() method, and I'm going to set a to this.refs.a.value.

update(){
    this.setState({
      a: this.refs.a.value,
      b: this.refs.b.value
    })
  }
  render(){ return (
      <div>
        <input
          ref="a"
          type="text"
          onChange={this.update.bind(this)}
        /> {this.state.a}
        <hr />
        <input
          ref="b"
          type="text"
          onChange={this.update.bind(this)}
        /> {this.state.b}
      </div>
    ) 
  }

ref actually returns the node that we're referencing, here, I can say this.refs.b.value. Now, in the browser, when I type in the a field, it's updating our a state. When I type in the b field, it's updating our b state. The ref attribute or prop can also take a callback.

Updating seperate state values

Like I said before, it's returning the node or component that we're referencing, we get the node, we could take that, and here in our callback method, we could say this.a is equal to the node, and now for a, we can just call it, this.a. We get back that node, we can use the DOM method of value. Save that, and everything works as expected.

update(){
    this.setState({
      a: this.a.value,
      b: this.refs.b.value
    })
  }
  render(){ 
    return (
      <div>
        <input
          ref={node => this.a = node}
          type="text"
          onChange={this.update.bind(this)}
        /> {this.state.a}

     ...

    ) 
  }

We can also reference an instance of another component, here, let's create a quick component. We're going to call it Input, we're going to return an <input>, and on this, we'll just have an onChange equal to this.props.Update.

class Input extends React.Component {
  render(){
    return <input type="text" onChange={this.props.update} />
  }
}

Up here, we're going to change this a input or input component, and now, since we're referencing a component, this is better represented by component rather than node. We'll say component there, and then, we're going to pass in this update, we don't need the type.

<Input
  ref={ component => this.a = component}
  update={this.update.bind(this)}
/>  {this.state.a}

Now, It's going to run that, and we're going to see that it's not exactly going to work. The b component is still working just fine, but here, a is no longer a node, it's now a component.

One way we can get at that, is by bringing in ReactDOM, and then, wrapping this.a in ReactDOM.findDOMNode(this.a), which is our component, and then, get the value off of that.

update(){
  this.setState({
    a: ReactDOM.findDOMNode(this.a).value,
    b: this.refs.b.value
  })
}

Now, if we type in, here we get that value. The reason we're going to getting away with that is that, we are returning a single node here.

When we get that component back, and we do findDOMNode on it, there's just one DOMNode there. But if we wrap this guy in a <div>, that findDOMNode call is referencing the <div> now, which has no value. One thing we could do, is put a ref on this guy, we'll call it input.

class Input extends React.Component {
  render(){
    return <div><input ref="input" type="text" onChange={this.props.update} /></div>
  }
}

Now, what we could do is, we can actually strip this back down, get rid of the ReactDOM find node part, and we could say, this.a which is our component.ref, we're getting the refs of our a component input, which is going to be that input field, and get its value.

update() {
  this.setState({
    a: this.a.refs.input.value,
    b: this.refs.b.value
  })
}

We save that, we type in our a field, we get our a. We type in our b field, we get our b.



HEY, QUICK QUESTION!
Joel's Head
Why are we asking?