136

I am starting with React.js and i want to do a simple form but in the documentation I have found two ways of doing it.

The first one is using Refs:

var CommentForm = React.createClass({
  handleSubmit: function(e) {
    e.preventDefault();
    var author = React.findDOMNode(this.refs.author).value.trim();
    var text = React.findDOMNode(this.refs.text).value.trim();
    if (!text || !author) {
      return;
    }
    // TODO: send request to the server
    React.findDOMNode(this.refs.author).value = '';
    React.findDOMNode(this.refs.text).value = '';
    return;
  },
  render: function() {
    return (
      <form className="commentForm" onSubmit={this.handleSubmit}>
        <input type="text" placeholder="Your name" ref="author" />
        <input type="text" placeholder="Say something..." ref="text" />
        <input type="submit" value="Post" />
      </form>
    );
  }
});

And the second one is using state inside the React component:

var TodoTextInput = React.createClass({
  getInitialState: function() {
    return {
      value: this.props.value || ''
    };
  },

  render: function() /*object*/ {
    return (
      <input className={this.props.className}
      id={this.props.id}
      placeholder={this.props.placeholder}
      onBlur={this._save}
      value={this.state.value}
      />
    );
  },

  _save: function() {
    this.props.onSave(this.state.value);
    this.setState({value: ''
  });
});

I can't see the pros and cons of the two alternatives, if some exists. Thanks.

Santanu Sahoo
  • 1,137
  • 11
  • 29
gabrielgiussi
  • 9,245
  • 7
  • 41
  • 71
  • Am I missing something here? Why don't you use the event object to get the form values? That seems to be the only reason to use a form here in the first place. If you are not using the default submit behaviour and have refs on the inputs you don't need to wrap them in a form. – Russell Ormes Jan 12 '16 at 02:01

4 Answers4

151

The short version: avoid refs.


They're bad for maintainability, and lose a lot of the simplicity of the WYSIWYG model render provides.

You have a form. You need to add a button that resets the form.

  • refs:
    • manipulate the DOM
    • render describes how the form looked 3 minutes ago
  • state
    • setState
    • render describes how the form looks

You have an CCV number field in an input and some other fields in your application that are numbers. Now you need to enforce the user only enters numbers.

  • refs:
    • add an onChange handler (aren't we using refs to avoid this?)
    • manipulate dom in onChange if it's not a number
  • state
    • you already have an onChange handler
    • add an if statement, if it's invalid do nothing
    • render is only called if it's going to produce a different result

Eh, nevermind, the PM wants us to just do a red box-shadow if it's invalid.

  • refs:
    • make onChange handler just call forceUpdate or something?
    • make render output based on... huh?
    • where do we get the value to validate in render?
    • manually manipulate an element's className dom property?
    • I'm lost
    • rewrite without refs?
    • read from the dom in render if we're mounted otherwise assume valid?
  • state:
    • remove the if statement
    • make render validate based on this.state

We need to give control back to the parent. The data is now in props and we need to react to changes.

  • refs:
    • implement componentDidMount, componentWillUpdate, and componentDidUpdate
    • manually diff the previous props
    • manipulate the dom with the minimal set of changes
    • hey! we're implementing react in react...
    • there's more, but my fingers hurt
  • state:
    • sed -e 's/this.state/this.props/' 's/handleChange/onChange/' -i form.js

People think refs are 'easier' than keeping it in state. This may be true for the first 20 minutes, it's not true in my experience after that. Put your self in a position to say "Yeah, I'll have it done in 5 minutes" rather than "Sure, I'll just rewrite a few components".

Brigand
  • 84,529
  • 20
  • 165
  • 173
  • Hi @FakeRainBrigand, thanks for the answer. When you say that in refs we manipulate the DOM, you're talking about Virtual DOM, don't you? Sure, if there are any changes in the Virtual DOM this implies a change in the "real" DOM too, but the same goes for state. – gabrielgiussi Apr 08 '15 at 15:16
  • 3
    Could you explain a little more about sed -e 's/this.state/this.props/' 's/handleChange/onChange/' -i form.js? – gabrielgiussi Apr 08 '15 at 15:22
  • 1
    No I mean actual changes to the dom. `React.findDOMNode(this.refs.foo)`. If you e.g. change `this.refs.foo.props.bar` nothing will happen. – Brigand Apr 08 '15 at 18:53
  • 1
    e.g. if you have `` change it to ``, or modify your handleChange function(s) to call the callback(s) in props. Either way, it's a few small obvious changes. – Brigand Apr 08 '15 at 18:57
  • 1
    And if writing onChange functions for multi-field forms is annoying, one can just use [React.addons.LinkedStateMixin](http://facebook.github.io/react/docs/two-way-binding-helpers.html). [High-order function](http://stackoverflow.com/a/25187443/466505) is also an option. – kolobos May 05 '15 at 08:45
  • @FakeRainBrigand Refs are okay if you absolutely have to measure DOM elements or perform DOM manipulations. As a simple example, do you know how to make an animated vertically collapsing component without measuring its content element height? As far as I know that's the only way to do it. – Andy Jun 11 '15 at 18:08
  • @Andy, I just meant that between defaultValue+ref (declarative init, imperative update, imperative get, state in dom) and value+onChange (declarative init, declarative update, declarative get, state in js), that latter is the better default in react. Refs are a lower level primitive that can be used to build declarative apis (such as using the size of elements). – Brigand Jun 12 '15 at 01:46
  • @FakeRainBrigand cool, yeah, I always try to do things declaratively if I can but I've already run into several cases where refs were necessary. – Andy Jun 12 '15 at 03:23
  • 4
    Not sure if I'm the only one finding your answer a little confusing. Could you show some code samples making your points clearer ? – Rishabh Jul 09 '15 at 14:46
  • Any of the points in particular? I'm not going to write code samples for each – Brigand Jul 10 '15 at 02:01
  • 2
    Having 50+ inputs on a screen an rendering each on any state change is undesirable. Componentizing each `input` field where each maintains its own state is ideal. At some point we do need to reconcile these various independent states with some larger model. Maybe we have an autosave on a timer, or we just save on `componentWillUnmount` This is where I find `refs` ideal, during reconcilation we pluck the `state` value from each `ref`, and none is the wiser. I agree in most cases `state` is the answer, but with a large number of `inputs`, utilizing a proper `refs` pattern is a performance boon – lux Mar 10 '16 at 05:28
  • Great answer. I just wanted to add another related article to this question that [showcases the ref attribute](https://www.robinwieruch.de/react-ref-attribute-dom-node/) again and how to exchange it with React's local state. – Robin Wieruch Sep 29 '17 at 15:07
  • You've given some good examples. However, like some people already said, "ref" isn't bad by definition - you just have to know how to use it and understand how does it differ from using a "state". Re-rendering the component each time you handle input or creating a functional component for each input field is neither a good practice, nor optimal in terms or code structure or application performance. – PiWo Sep 24 '18 at 14:09
  • @Brigand can you please suggest an example of how to wire a component with multiple inputs using state and not have a laggy performance. – srinivas Jun 21 '20 at 18:07
  • I beg to differ. `refs` have their place and saying "avoid using refs" might not be the right thing to say. If you go by Finite state machines, `state` is finite while `ref` is the context. State affects the UI directly while ref doesn't – apnerve Apr 05 '22 at 07:41
123

I've seen a few people cite the above answer as a reason to "never use refs" and I want to give my (as well as a few other React devs I've spoken to) opinion.

The "don't use refs" sentiment is correct when talking about using them for component instances. Meaning, you shouldn't use refs as a way to grab component instances and call methods on them. This is the incorrect way to use refs and is when refs go south quickly.

The correct (and very useful) way to use refs is when you're using them to get some value from the DOM. For example, if you have an input field attaching a ref to that input then grabbing the value later through the ref is just fine. Without this way, you need to go through a fairly orchestrated process for keeping your input field up to date with either your local state or your flux store - which seems unnecessary.

2019 edit: Hello friends of the future. In addition to what I mentioned a few years ago ^, with React Hooks, refs are also a great way to keep track of data between renders and aren't limited to just grabbing DOM nodes.

Tyler McGinnis
  • 34,836
  • 16
  • 72
  • 77
  • 3
    Your last paragraph makes perfect sense, but can you clarify your second paragraph? What is a concrete example of grabbing a component instance and calling a method that would be considered incorrect? – Daynil Jan 08 '16 at 03:16
  • 2
    I'd agree with this. I use refs unless/until I need to do validation or manipulation of a field's value. If I do need to validate on change or change values programmatically, then I use state. – Christopher Davies Feb 11 '16 at 16:27
  • 1
    I agree with this as well. During a discovery phase I've purposely approached a screen with a large number of inputs with naivete. All input values stored in a map (in state) keyed by id. Needless to say, performance suffered since setting state and rendering 50+ inputs (some material-ui, which were heavy!) on such minor UI changes as a checkbox click was not ideal. Componentizing each input that can maintain its own state seemed the proper approach. If reconciliation is needed, just peer into the `refs` and get the state value. It seems like a really nice pattern actually. – lux Mar 10 '16 at 05:37
  • 2
    I completely agree. The accepted answer is too vague in my opinion. – James Wright Apr 10 '17 at 18:23
  • I agree. While designing a general Form component, this brings to light the pain points of controlled components and managing focus, error handling etc. Not possible to have a clean architecture in fact. Talk to me if required. I'm moving my components to refs. – HalfWebDev Jan 31 '20 at 11:10
16

This post is old.

I will share my little experience on one case on that matter.

I was working on a big component (414 lines) with a lot of 'dynamic' inputs and lots of cached data involved. (I am not working alone on the page, and my senses tell me that the structure of the code probably could be splitted better, but it's not the point (well, it could be but I am dealing with it)

I first worked with state to handle the values of the inputs:

  const [inputsValues, setInputsValues] = useState([])
  const setInputValue = (id, value) => {
    const arr = [...inputsValues]
    arr[id] = value
    setInputsValues(arr)
  }

and of course in the inputs:

value={inputsValues[id] || ''}
onChange={event => setInputValue(id, event.target.value)}

Rendering was so heavy that the input change was choppy as **** (don't try to keep the key down, text would only appear after a pause)

I was sure I could avoid this using refs.

ended up like this :

  const inputsRef = useRef([])

and in the inputs :

ref={input => (inputsRef.current[id] = input)}

[ well in My case Input was Material-UI TextField so it was:

inputRef={input => (inputsRef.current[id] = input)}

]

Thanks to this, there is no rerendering, the input is smooth, funcionality works the same way. It will save cycles and calculation, so energy too. Do it for earth x)

My conclusion : useRef for inputs value can even be be needed.

Kaphar
  • 161
  • 1
  • 5
  • I had the same issue, I managed it with useReducer hook. https://blog.logrocket.com/react-usereducer-hook-ultimate-guide/ While I respect your opinion I would (still) avoid refs... The issue is that once the refs are in, people will use them randomly without even understanding their real purpose, with all the resulting issues... – Denis Apr 29 '22 at 10:28
  • @Kaphar I like the idea. I'm also working on same type of functionality with the big form and list of state. currently I'm using the first value/onChange approach and having same rendering issue. I'm just wondering does useRef will solve the issue. Consider on some input change you want to calculate some total and show. How would that work with useRef? Any suggestion. Thanks! – Utsav Patel Feb 23 '23 at 20:32
8

TL;DR Generally speaking, refs go against React's declarative philosophy, so you should use them as a last resort. Use state / props whenever possible.


To understand where yo use refs vs state / props, let's look at some of the design principles that React follows.

Per React documentation about refs

Avoid using refs for anything that can be done declaratively.

Per React's Design Principles about Escape Hatches

If some pattern that is useful for building apps is hard to express in a declarative way, we will provide an imperative API for it. (and they link to refs here)

Which means React's team suggest to avoid refs and use state / props for anything that can be done in a reactive / declarative way.

@Tyler McGinnis has provided a very good answer, stating as well that

The correct (and very useful) way to use refs is when you're using them to get some value from the DOM...

While you can do that, you'll be working against React's philosophy. If you have value in an input, it most certainly comes from state / props. To keep code consistent and predictable, you should stick to state / props there as well. I acknowledge the fact that refs sometimes gives you the quicker solution, so if you do a proof of concept, quick and dirty is acceptable.

This leaves us with several concrete use cases for refs

Managing focus, text selection, or media playback. Triggering imperative animations. Integrating with third-party DOM libraries.

Community
  • 1
  • 1
Lyubomir
  • 19,615
  • 6
  • 55
  • 69