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
?
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.
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.
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.
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:
<JInput input={radioInput1} handleChange={this.handleValueChange} /> Set
<JInput input={radioInput2} handleChange={this.handleValueChange} /> Set/Key
<JInput input={radioInput3} handleChange={this.handleValueChange} /> Key
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); }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With