How to properly validate input values with React.JS?

EasilyBaffled picture EasilyBaffled · Jun 3, 2014 · Viewed 132.1k times · Source

I have a simple form. All of the components and state are held in the Page component. There are 2 display headers and 3 input fields. The first input is supposed to be text, and the second and third are supposed to be ints. When the user inputs the wrong type of data, I want to have an error message pop up next to the input field. My questions relate to best practices in React.JS

Who decides that the value is in valid? I suppose that the only job of the input field is to direct the value back to component holding the state, so does this mean that only Page can determine if a value is valid?

How should I then have the pop up appear? Should Page have to trigger a new boolean state element that will be passed through perp that will tell Adaptive_Input to reveal the error message?

JSFiddle

JS:

/**
 * @jsx React.DOM
 */
var Adaptive_Input = React.createClass({ 
    handle_change: function(){
        var new_text = this.refs.input.getDOMNode().value;
        this.props.on_Input_Change(new_text);
    },
    render: function(){
        return (
                <div className='adaptive_placeholder_input_container'>
                    <input 
                        className="adaptive_input"
                        type="text" 
                        required="required" 
                        onChange= {this.handle_change}
                        ref="input"
                    ></input>
                    <label
                        className="adaptive_placeholder"
                        alt={this.props.initial}
                        placeholder={this.props.focused}
                    ></label>
                </div>              
                );
    }
});

var Form = React.createClass({
    render: function(){
        return (
                <form>
                    <Adaptive_Input
                        initial={'Name Input'}
                        focused={'Name Input'}
                        on_Input_Change={this.props.handle_text_input}
                    />
                    <Adaptive_Input
                        initial={'Value 1'}
                        focused={'Value 1'}
                        on_Input_Change={this.props.handle_value_1_input}
                    />
                    <Adaptive_Input
                        initial={'Value 2'}
                        focused={'Value 2'}
                        on_Input_Change={this.props.handle_value_2_input}
                    />
                </form>
                );
    }
});

var Page = React.createClass({
    getInitialState: function(){
        return {
            Name : "No Name",
            Value_1 : '0',
            Value_2 : '0',
            Display_Value: '0'
        };
    },
    handle_text_input: function(new_text){
        this.setState({
                Name: new_text
            });
    },
    handle_value_1_input: function(new_value){
        console.log("===");
        var updated_display = parseInt(new_value) + parseInt(this.state.Value_2);
        updated_display = updated_display.toString();
        this.setState({
                Display_Value: updated_display 
            });
    },
    handle_value_2_input: function(new_value){
        var updated_display = parseInt(this.state.Value_1) + parseInt(new_value);
        updated_display = updated_display.toString();
        this.setState({
                Display_Value: updated_display
            });
    },
    render: function(){
        return(
                <div>
                    <h2>{this.state.Name}</h2>
                    <h2>Value 1 + Value 2 = {this.state.Display_Value}</h2>
                    <Form
                        handle_text_input={this.handle_text_input}
                        handle_value_1_input = {this.handle_value_1_input}
                        handle_value_2_input = {this.handle_value_2_input}
                    />
                </div>
        );
    }
});

React.renderComponent(<Page />, document.body);

Answer

z5h picture z5h · Jun 12, 2014

First, here is an example of what I'll mention below: http://jsbin.com/rixido/2/edit

How to properly validate input values with React.JS?

However you want. React is for rendering a data model. The data model should know what is valid or not. You can use Backbone models, JSON data, or anything you want to represent the data and it's error state.

More specifically:

React is generally agnostic towards your data. It's for rendering and dealing with events.

The rules to follow are:

  1. elements can change their state.
  2. they cannot change props.
  3. they can invoke a callback that will change top level props.

How to decide if something should be a prop or a state? Consider this: would ANY part of your app other than the text field want to know that the value entered is bad? If no, make it a state. If yes, it should be a prop.

For example, if you wanted a separate view to render "You have 2 errors on this page." then your error would have to be known to a toplevel data model.

Where should that error live?
If your app was rendering Backbone models (for example), the model itself would have a validate() method and validateError property you could use. You could render other smart objects that could do the same. React also says try to keep props to a minimum and generate the rest of the data. so if you had a validator (e.g. https://github.com/flatiron/revalidator) then your validations could trickle down and any component could check props with it's matching validation to see if it's valid.

It's largely up to you.

(I am personally using Backbone models and rendering them in React. I have a toplevel error alert that I show if there is an error anywhere, describing the error.)