React: クリックハンドラーのevent.preventDefaultは、onChangeの呼び出しを妨げません

作成日 2017年02月17日  ·  6コメント  ·  ソース: facebook/react

機能をリクエストしバグを報告しますか?

バグ!

現在の動作は何ですか?

onClickおよびonChangeハンドラーを使用してタイプcheckbox input要素をレンダリングすると、 event.preventDefault()あっても、 onChangeが呼び出されます。 event.preventDefault()onClickハンドラーで呼び出されます。

現在の動作がバグである場合は、 https://jsfiddle.netまたは同様の方法(テンプレート:https://jsfiddle.net/reactjs/69z2wepo/)を介して、再現する手順と、可能であれば問題の最小限のデモを提供してください。

http://jsfiddle.net/rf3w7apc/

期待される動作は何ですか?

onClickハンドラーでevent.preventDefaultを呼び出すと、デフォルトのアクションが発生しないようにする(またはその効果を元に戻す)必要があります。これは、 input要素の値を更新することです。 これにより、 changeイベントリスナーが呼び出されなくなります。 予想される動作については、 https://jsfiddle.net/L1eskzsq/を参照して

この問題の影響を受けるReactのバージョンとブラウザ/ OSはどれですか?

マスターからのビルド、macOS 10.12.2を使用してテストし、以下で検証しました。

  • Chrome 56.0.2924.87(64ビット)
  • Firefox 51.0.1(64ビット)

Safari 10.0.2は、どちらの場合もchangeイベントリスナーを呼び出します。

DOM Bug

最も参考になるコメント

これは関係がありますか? チェックボックスonChange preventDefault呼び出されると、Reactとブラウザーの状態が同期しなくなります//codesandbox.io/s/0qpr936xkv

全てのコメント6件

changeイベントは、 onClickハンドラーが呼び出される前に(つまり、 preventDefault前に)、 topChangeへの応答としてChangeEventPluginによって作成されます。 preventDefault call)、ここ: https

このプラグインは、チェックボックスの値が変更されるかどうかを判断しようとしています。変更される場合は、 changeイベントをキューに入れます。 inputValueTracking機能を使用して、前の値を次の値と比較し、この決定を行います: https

nextValueは、次のようにDOMから直接取得されます。 value = isCheckable(node) ? '' + node.checked : node.value; https://github.com/facebook/react/blob/master/src/renderers/dom/shared/inputValueTracking.js# L56

問題は、この時点でnode.checked値が一時的に真であるため、最初のクリック後は次のようになることです。

lastValue == false (正解)
nextValue == true (不正解)

チェックボックスを次にクリックすると、値が次のようになるため、 changeイベントは発生しなくなります。

lastValue == true (不正解)
nextValue == true (不正解)

全体的な問題は、 onClickのpreventDefaultを呼び出す前に、Reactがノードのchecked値をDOMに要求していることのようです。

これを掘り下げるのを手伝ってくれた@karelskopek@Mintaffeeに感謝します。

@awearyこれは解決する価値があると思いますか?

これで進歩はありませんか? React v16.2を使用していますが、動作は同じです。onChangeは最初にトリガーされ、その後のクリックではトリガーされません。 ここではかなり優先度の高い問題だと思います。

これは関係がありますか? チェックボックスonChange preventDefault呼び出されると、Reactとブラウザーの状態が同期しなくなります//codesandbox.io/s/0qpr936xkv

React16.8.6ではまだ問題です。 考えられる回避策の1つは、 onClickハンドラーですべてを実行することです。

const radios = ['one', 'two', 'three'];

class Row extends Component {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  render() {
    const { value } = this.props;
    return (
      <div className="row">
        <div className="radio-group">
          {radios.map(radio => (
            <label key={radio} className="radio">
              <input
                type="radio"
                name="radio"
                value={radio}
                checked={radio === value}
                onClick={this.onClick}
                onChange={this.onChange}
              /><span>Radio</span>
            </label>
          ))}
        </div>
      </div>
    );
  }

  onClick(event) {
    // Check something to prevent radio change
    if (...) {
      event.preventDefault();
      return;
    }
    // Sync value in the store
    this.props.setValue(event.target.value);
  }
  onChange() {
    // We need this empty handler to suppress React warning:
    // "You provided a `checked` prop to a form field without an `onChange` handler.
    // This will render a read-only field. If the field should be mutable use `defaultChecked`.
    // Otherwise, set either `onChange` or `readOnly`"
  }
}

制御されたコンポーネントを使用

onChange(event) {
  // Check something to prevent radio change
  if (...) {
    return;
  }
  // Sync value in the store
  this.props.setDateTime(event.target.value);
}

より簡単な解決策として、onMouseDownイベントでe.preventDefaultを実行して、onChangeが発生しないようにすることができます。

onMouseDown(e) { if (...) { e.preventDefault(); return; } }

このページは役に立ちましたか?
0 / 5 - 0 評価