Use state or refs in React.js form components?

Reactjs

Reactjs Problem Overview


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.

Reactjs Solutions


Solution 1 - Reactjs

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".

Solution 2 - Reactjs

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.

Solution 3 - Reactjs

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.

Solution 4 - Reactjs

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiongabrielgiussiView Question on Stackoverflow
Solution 1 - ReactjsBrigandView Answer on Stackoverflow
Solution 2 - ReactjsTyler McGinnisView Answer on Stackoverflow
Solution 3 - ReactjsKapharView Answer on Stackoverflow
Solution 4 - ReactjsLyubomirView Answer on Stackoverflow