Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Plug in or pattern for dealing with large forms in React?

Tags:

reactjs

I have a form with a number of inputs, and I want to collect the values of the inputs in my state, without having to define an onChange or custom component for each input.

React has an official add-on LinkedStatesMixin that seems to be on its way out, and is only a partial solution (missing radio button groups, for example):

https://github.com/facebook/react/issues/2302

https://github.com/facebook/react/issues/3573

Meanwhile coming from Angular and Ember, an elegant and concise API for setting up input bindings is a must-have feature in a front-end framework.

What's the most React-ish way to accomplish this without using the deprecated LinkedStatesMixin?

like image 564
Gabe Kopley Avatar asked Sep 23 '15 00:09

Gabe Kopley


People also ask

Which design pattern is used in React?

The higher-order component, or HOC pattern, is an advanced React pattern used for reusing component logic across our application. The HOC pattern is useful for cross-cutting concerns — features that require sharing of component logic across our application.

Does React use state pattern?

React useState pattern for internal and external changes (to avoid stale state) Bookmark this question. Show activity on this post. This is ok until another form component alters the value represented by this component.

How do you handle form validation in React?

Form validation in React allows an error message to be displayed if the user has not correctly filled out the form with the expected type of input. There are several ways to validate forms in React; however, this shot will focus on creating a validator function with validation rules.


1 Answers

This is one way of doing it. This is from https://github.com/calitek/ReactPatterns React.13.Common/FormInputs.

jInput.js

    import React, {Component} from 'react';

    class JInputRender extends Component {
        binder(...methods) { methods.forEach( (method) => this[method] = this[method].bind(this) ); }

        render() {
            var inputSty = this.props.input.style ? this.props.input.style : {color: 'red'};
            var textValue = this.state.textValue;
            var colorValue = this.props.input.colorValue ? this.props.input.colorValue : '#1A3212';
            var checkedValue = (this.props.input.checkedValue != null) ? this.props.input.checkedValue : false;
            var numberValue = this.props.input.numberValue ? this.props.input.numberValue : 0;
            var radioValue = this.props.input.radioValue ? this.props.input.radioValue : '';
            var radioChecked = (this.props.input.radioChecked != null) ? this.props.input.radioChecked : false;
            var min = this.props.input.min ? this.props.input.min : 0;
            var max = this.props.input.max ? this.props.input.max : 100;
            var step = this.props.input.step ? this.props.input.step : 1;
            var inputType = this.props.input.type ? this.props.input.type : 'text';

            var returnRadio = (
                    <input
                        type={inputType}
                        ref='inputRef'
                        style={inputSty}
                        checked={radioChecked}
                        value={radioValue}
                        onChange={this.handleValueChange} />
                )

            var returnChecked = (
                    <input
                        type={inputType}
                        ref='inputRef'
                        style={inputSty}
                        checked={checkedValue}
                        onChange={this.handleCheckedChange} />
                )

            var returnColor = (
                    <input
                        type={inputType}
                        ref='inputRef'
                        style={inputSty}
                        value={colorValue}
                        onChange={this.handleValueChange} />
                )

            var returnNumber = (
                    <input
                        type={inputType}
                        ref='inputRef'
                        style={inputSty}
                        value={numberValue}
                        min={min} max={max} step={step}
                        onChange={this.handleValueChange} />
                )

            var returnText = (
                    <input
                        type={inputType}
                        ref='inputRef'
                        style={inputSty}
                        value={textValue}
                        onChange={this.handleTextValueChange} />
                )
            var returnIt;
            switch (inputType) {
                case 'checkbox': returnIt = returnChecked; break;
                case 'radio': returnIt = returnRadio; break;
                case 'color': returnIt = returnColor; break;
                case 'number':
                case 'range': returnIt = returnNumber; break;
                default: returnIt = returnText; break;
            }

            return (returnIt);
        }
    }

    export default class JInput extends JInputRender {
        constructor() {
          super();
            this.state = {textValue: ''};
          this.binder('handleCheckedChange', 'handleTextValueChange', 'handleValueChange');
        }

        componentDidMount() {
            if (this.props.input.textValue) this.setState({textValue: this.props.input.textValue});
            if (this.props.input.focus) React.findDOMNode(this.refs.inputRef).focus();
        }
        componentWillReceiveProps(nextProps) {
            if (nextProps.input.textValue && (this.state.textValue != nextProps.input.textValue))
                {this.setState({textValue: nextProps.input.textValue});}
        }
        handleCheckedChange(event) { this.props.handleChange(this.props.input.name, event.target.checked); }
        handleTextValueChange(event) {
            var newValue = event.target.value;
            this.setState({textValue: newValue});
            this.props.handleChange(this.props.input.name, newValue);
        }
        handleValueChange(event) { this.props.handleChange(this.props.input.name, event.target.value); }
    }

app.ctrl.js. Just skip the JRadioGroup stuff.

    import React, {Component} from 'react';

    import Actions from './../flux/Actions';
    import JInput from './common/jInput';
    import JRadioGroup from './common/jRadioGroup';

    import BasicStore from './../flux/Basic.Store';

    var AppCtrlSty = {
        height: '100%',
        padding: '0 10px 0 0'
    }

    var checkBoxSty = {
        boxSizing: 'border-box',
        display: 'inline-block',
        lineHeight: '18px',
        marginLeft: '2px',
        outline: 'none',
        position: 'relative'
    };

    var radioSty = {color: "blue"}

    var input3Sty = {color: 'green'};

    var inputLabel = {margin: '0 5px'};

    var textInput1 = {name: 'text', type: 'text', textValue: '', focus: true};
    var checkInput1 = {name: 'checkbox', type: 'checkbox', style: checkBoxSty};
    var colorInput = {name: 'color', type: 'color'};
    var numberInput = {name: 'number', type: 'number', min: 0, max: 100};
    var rangeInput = {name: 'range', type: 'range', min: 0, max: 100};

    var radioInput1 = {name: 'radioGroup', type: 'radio', radioValue: 'set'};
    var radioInput2 = {name: 'radioGroup', type: 'radio', radioValue: 'setkey'};
    var radioInput3 = {name: 'radioGroup', type: 'radio', radioValue: 'key'};

    class AppCtrlRender extends Component {
        binder(...methods) { methods.forEach( (method) => this[method] = this[method].bind(this) ); }

        render() {
            var inputData = this.state.data;

            textInput1.textValue = inputData.text;
            checkInput1.checkedValue = inputData.checkbox;
            colorInput.colorValue = inputData.color;
            numberInput.numberValue = inputData.number;
            rangeInput.numberValue = inputData.range;

            radioInput1.radioChecked = inputData.radioGroup == 'set';
            radioInput2.radioChecked = inputData.radioGroup == 'setkey';
            radioInput3.radioChecked = inputData.radioGroup == 'key';

            var selected = inputData.checkbox ? 'true' : 'false';
            var radioGroupName1 = 'key1'; //must be distinct for each use of JRadioGroup
            var radioValue = inputData.radioGroup;
            return (
                <div id='AppCtrlSty' style={AppCtrlSty}>
                    React 1.3 Form input<br/><br/>
                    Text: <JInput input={textInput1} handleChange={this.handleValueChange} /><br/><br/>
                    Checkbox: <JInput input={checkInput1} handleChange={this.handleValueChange} /> Value: {selected}<br/><br/>
                    Color: <JInput input={colorInput} handleChange={this.handleValueChange} /> Value: {colorInput.colorValue}<br/><br/>
                    Number: <JInput input={numberInput} handleChange={this.handleValueChange} /> Value: {numberInput.numberValue}<br/><br/>
                    Range: <JInput input={rangeInput} handleChange={this.handleValueChange} /> Value: {rangeInput.numberValue}<br/><br/>
              <JRadioGroup name={radioGroupName1} value={radioValue} ref="keyGroup" onChange={this.handleRadioChange}>
                <div>Radio Group:
                  <label id='inputLabel1' style={inputLabel}>
                    <input type="radio" value="set" /> Set
                  </label>
                  <label id='inputLabel2' style={inputLabel}>
                    <input type="radio" value="setkey"/> Set/Key
                  </label>
                  <label id='inputLabel3' style={inputLabel}>
                    <input type="radio" value="key"/> Key
                  </label>
                </div>
              </JRadioGroup><br/><br/>

                    Radio Input: &nbsp;
                    <JInput input={radioInput1} handleChange={this.handleValueChange} /> Set &nbsp;
                    <JInput input={radioInput2} handleChange={this.handleValueChange} /> Set/Key &nbsp;
                    <JInput input={radioInput3} handleChange={this.handleValueChange} /> Key &nbsp;
                    Value: {radioValue}
                </div>
            );
        }
    }

    function getState() { return {data: BasicStore.getData()}; };

    export default class AppCtrl extends AppCtrlRender {
        constructor() {
          super();
            this.state = getState();
          this.binder('storeDidChange', 'handleValueChange', 'handleRadioChange');
        }

        componentDidMount() {
            this.unsubscribe = BasicStore.listen(this.storeDidChange);
        }
        componentWillUnmount() { this.unsubscribe(); }
        storeDidChange() { this.setState(getState()); }
      handleRadioChange(event) { Actions.editRecord('type', event.target.value); }
        handleValueChange(name, value) { Actions.editRecord(name, value); }
    }
like image 98
J. Mark Stevens Avatar answered Nov 16 '22 03:11

J. Mark Stevens