Why does ReactJS handle the `checked` attr radio differently than other attrs?

tl; dr The reaction refuses honor checked={checkThisOption}on the inputs, even if it is very different data-ischecked={checkThisOption}from the same set of inputs.

I did not do this work on jsfiddle, but I reproduced the problem using this code .

long version

I have a simple ReactJS component that presents a list of radio buttons for the user. It is assumed that the user will be able to select a radio, and then press a button to confirm their choice.

Here's the def component (note: I'm using ES6 and webpack):

import React from 'react';

class Widget extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            currentValue: null // tracks currently-selected choice, by its value
        };
    }

    onClickOptionRadio = (event) => {
        this.setState({
            currentValue: String(event.currentTarget.value)
        });
    }

    onConfirm = (event) => {
        if(!this.props.onChange) return;
        this.props.onChange(this.state.currentValue);
    };

    render() {
        let choices = this.props.choices;
        let currentValue = this.state.currentValue;

        return (
            <div className="Widget">
                <ol className="choices">
                    {
                        choices.map((choice, i) => {
                            // decide whether to mark radio as checked:
                            // - if no current choice, check first radios
                            // - otherwise, check radio matching current choice
                            let noCurrentChoice      = (currentValue === null);
                            let drawingFirstChoice   = (i === 0);
                            let thisChoiceIsSelected = (String(choice.value) === currentValue);
                            let checkThisOption      = thisChoiceIsSelected || (noCurrentChoice && drawingFirstChoice);

                            return (
                                <li key={i}>
                                    <input type="radio" name="choices"
                                        value={choice.value}
                                        onChange={this.onClickOptionRadio}
                                        checked={checkThisOption?'checked':''}
                                        data-ischecked={checkThisOption}
                                        />

                                    <label>{choice.label}</label>
                                    {' '}

                                    {checkThisOption ? 'CHECKED' : ''}
                                </li>
                            );
                        })
                    }
                </ol>

                <button onClick={this.onConfirm}>Confirm choice</button>

            </div>
        );
    }

}

export default Widget;

Here's the owning component:

import React from 'react';
import Widget from 'components/widget';

class Owner extends React.Component {
    constructor(props) {
        super(props);
        this.state = {};
    }

    render() {
        let choices = [
            { value: 10, label: 'First' },
            { value: 20, label: 'Second' },
            { value: 30, label: 'Third' }
        ];

        return (
            <div className="Owner">

                <Widget
                    choices={choices}
                    />

            </div>
        );
    }

}

export default Owner;

Here's the gif of this in action:

Live demo

Pay attention to a few things from the video:

  • the logic obviously works to test the first radio on the original render
  • , .
  • , , margin-right: 2rem , .
  • , , ,
  • , componentWillUpdate ;

, , , Widget , . , data- attr , , , . , , , , , React .

, ? , - Widget onChange, .

. , : , , (, , , ..), , .

, . onChange(event, newValue), . , , React checked attr , attrs .

, onChange, : , . , , currentValue : . - , checked , . , , .

, , , . "" Flux Redux, " . , , . .

. , React , , , vanilla input s.

: name , . , .

+4
1

, :

enter image description here

:

class Widget extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      currentValue: null // tracks currently-selected choice, by its value
    };
    this.onClickOptionRadio = this.onClickOptionRadio.bind(this)
    this.onConfirm = this.onConfirm.bind(this)
  }

  onClickOptionRadio (event) {
    this.setState({
      currentValue: String(event.currentTarget.value)
    });
  }

  onConfirm (event) {
    if(!this.props.onChange) return;
    this.props.onChange(this.state.currentValue);
  };

  render() {
    let choices = this.props.choices;
    let currentValue = this.state.currentValue;

    return (
      <div className="Widget">
      <ol className="choices">
      {
        choices.map((choice, i) => {
          // decide whether to mark radio as checked:
          // - if no current choice, check first radios
          // - otherwise, check radio matching current choice
          let noCurrentChoice      = (currentValue === null);
          let drawingFirstChoice   = (i === 0);
          let thisChoiceIsSelected = (String(choice.value) === currentValue);
          let checkThisOption      = thisChoiceIsSelected || (noCurrentChoice && drawingFirstChoice);

          return (
            <li key={i}>
            <input type="radio" name="choices"
            value={choice.value}
            onChange={this.onClickOptionRadio}
            checked={checkThisOption?'checked':''}
            data-ischecked={checkThisOption}
            />

            <label>{choice.label}</label>
            {' '}

            {checkThisOption ? 'CHECKED' : ''}
            </li>
          );
        })
      }
      </ol>

      <button onClick={this.onConfirm}>Confirm choice</button>

      </div>
    );
  }

}


class Owner extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    let choices = [
      { value: 10, label: 'First' },
      { value: 20, label: 'Second' },
      { value: 30, label: 'Third' }
    ];

    return (
      <div className="Owner">

      <Widget
      choices={choices}
      />

      </div>
    );
  }

}

ReactDOM.render(
  <Owner />,
  document.getElementById('container')
);

Chrome 47. jsfiddle.

+1

Source: https://habr.com/ru/post/1620833/


All Articles