This article is about the React component’s form single-line text input box. In terms of Input box values, probably most packaged ReactUI component libraries do nothing more than pass the Props to the Input component and then change the State of the current component in the onChange callback of the Input component. To change the Input component Props.

If the parent component is relatively complex and the value of one of the Input components is changed, updating State triggers render and any other child components that might be State related (Prop is passed through the parent component’s State). It can be scary that every keystroke we make in the input box triggers a sequence of code logic.

The need to pass values through Props

The Input box must have a value attribute. If this attribute is passed as Props or State, it must be changed by changing Props or State. There is only one way to change this attribute, which is to listen on the onChange of the Input. Forms-react:

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ' '};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}Copy the code

If we want to encapsulate the Input box as a separate component, the value of the component must be passed as Props. When we use the Props as the Input property, This can only be done by the parent component changing the Props passed, which inevitably generates some code logic for changing values and passing values.

General treatment of some good UI libraries

Now that we understand the value passing mechanism of the Input field, let’s see if some of the best component libraries in the community handle this as we expect, and if they have these immutable code logic problems.

React-Bootstrap

React Boostrap is the React version of Bootstrap, and its authority is evident. The official sample code is exactly as we expected, changing the parent component’s State via onChang callback, and then passing State as Props to the child component. When we open the source code, the key part is as follows:

class FormControl extends React.Component {
  render() {
      ...
    const {
      componentClass: Component, type, id = controlId, inputRef, className, bsSize, ... props } =this.props;

    const[bsProps, elementProps] = splitBsProps(props); . return (<Component
        {. elementProps}
        type={type}
        id={id}
        ref={inputRef}
        className={classNames(className, classes)} / >); }}Copy the code

It can be interpreted that the achievement is a tag encapsulation, without its own State, and its value is completely dependent on the changes of Props, which is also consistent with our expected results, and the mild result is that some questions we threw at the beginning, which seems not to be solved here.

ant-design

Take a look at the official sample code and see if it is a little excited. The code is as follows:

import { Input } from 'antd';

ReactDOM.render(<Input placeholder="Basic usage" />, mountNode);Copy the code

Seems to get rid of the way to pass Props by changing State in a callback onChange, and the code is clean and simple. Take a look at the source code, ant-Design, captured key parts of the code:

renderInput() {
    const { value, className } = this.props;
    const otherProps = omit(this.props, [
      'prefixCls'.'onPressEnter'.'addonBefore'.'addonAfter'.'prefix'.'suffix',]);if ('value' in this.props) {
      otherProps.value = fixControlledValue(value);
      // Input elements must be either controlled or uncontrolled,
      // specify either the value prop, or the defaultValue prop, but not both.
      delete otherProps.defaultValue;
    }
    return this.renderLabeledIcon(
      <input
        {. otherProps}
        className={classNames(this.getInputClassName(), className)}
        onKeyDown={this.handleKeyDown}
        ref="input"
      />,); }Copy the code

As you can see, there is no State in the component, and all the State is managed by Props. If we add an attribute in the official example, value=” XXXX “, then changing the value is the same as before.

react-ui

Is also a frame written by Chinese, relatively unknown. React-ui is a react-ui UI.

constructor (props) {
    super(props)
    this.handleChange = this.handleChange.bind(this)
  }

  handleChange (event) {
    const { type } = this.props

    let value = event.target.value
    if (value && (type === 'integer' || type === 'number')) {
      if(! Regs[type].test(value))return
    }
    this.props.onChange(value)
  }Copy the code

The only difference is that there are some grammatical differences, which may not be what we want to see.

Is there really a processing mechanism that is different from what we expect and can solve our problems?

MUI

To open the source code, the key parts of MUI are as follows:

constructor(props) {
    super(props);
    let value = props.value;
    let innerValue = value || props.defaultValue;

    if (innerValue === undefined) innerValue = ' ';

    this.state = {
      innerValue: innerValue,
      isTouched: false.isPristine: true}; . } componentWillReceiveProps(nextProps) {if ('value' in nextProps) this.setState({ innerValue: nextProps.value });
  }
  onChange(ev) {
    this.setState({
      innerValue: ev.target.value,
      isPristine: false}); . }Copy the code

As you can see, the UI handles this a little differently from what we’ve seen before. The difference is that it does extra processing to the Props. Value that is passed in, and it stuffs it inside the State, so that changing the input value can be done just by changing the State of the current component. Instead of changing back to the parent component to change State and pass in Props again. Is there a glimmer of relief to see here? But on second thought, it seems to complicate the problem:

  • Why do we need to evaluate the Props as a State? If we can evaluate the Props as a State, this property should not be a State.
  • ComponentWillReceiveProps again to deal with some logic judgment, and the logic are likely to be associated with its business, to decide whether to need to change the State, the logic is not we want to see.

metarial-design

The so-called flat extremely style, look inside the source code exactly how to deal with this problem, metarial-design:

handleRefInput = node= > {
    this.input = node;
    if (this.props.inputRef) {
      this.props.inputRef(node); }};let inputProps = {
      ref: this.handleRefInput, ... inputPropsProp, };return (
      <div onBlur={this.handleBlur} onFocus={this.handleFocus} className={className} {. other} >
        <InputComponent
          {. inputProps} / >
      </div>
    );Copy the code

The ref attribute is assigned to this.input=input (there is an extra function that does not affect it here). I’m just going to take the value directly from the DOM or do something to modify the DOM directly, right? . Finally, see if there is an official explanation.

The React website

Check carefully and find a relevant article stating Uncontroled Components. This article goes into great detail about our doubts (it seems that these doubts are not taken for granted, they are real).

In most cases, we recommend using controlled components to implement forms. In a controlled component, form data is handled by a React component. The alternative is uncontrolled components, where form data is handled by the DOM itself.

It should be obvious here, so correct posture:

render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" ref={(input) => this.input = input} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }Copy the code

After reading this, you can understand how material-design and ant-design are written. If you want the default values, as officially stated:

In the React rendering lifecycle, the value attribute on form elements will override the value in the DOM. With an uncontrolled component, you often want React to specify the initial value, but leave subsequent updates uncontrolled. To handle this case, you can specify a defaultValue attribute instead of value.

render() {
  return (
    <form onSubmit={this.handleSubmit}>
      <label>
        Name:
        <input
          defaultValue="Bob"
          type="text"
          ref={(input) => this.input = input} />
      </label>
      <input type="submit" value="Submit" />
    </form>
  );
}Copy the code

Back and forth, a direct defaultValue to solve our doubts, originally placed in the official website but seems to understand the specific scene is not clear, think too much and do too little.