反応jsでonchangeイベントをトリガーする最良の方法は何ですか


112

Backbone + ReactJSバンドルを使用して、クライアント側のアプリを構築します。悪名高い依存に大きく依存しvalueLinkて、双方向バインディングのReactJSインターフェイスをサポートする独自のラッパーを介してモデルに値を直接伝達します。

今私たちは問題に直面しました:

我々は持っているjquery.mask.js、プログラムのでそれは火がイベントを反応しない入力値をフォーマットプラグインを。これはすべて、モデルがユーザー入力からフォーマットされていない値を受け取り、プラグインからフォーマットされた値を見落とす状況につながります。

Reactにはブラウザによってはたくさんのイベント処理戦略があるようです。Reactがそれを聞くことができるように、特定のDOM要素の変更イベントをトリガーする一般的な方法はありますか?


回答:


174

React 16およびReact> = 15.6の場合

.value=Reactライブラリは入力値セッターをオーバーライドするため、セッターは期待どおりに機能しませんが、inputasコンテキストで関数を直接呼び出すことができます。

var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(input, 'react 16 value');

var ev2 = new Event('input', { bubbles: true});
input.dispatchEvent(ev2);

textarea要素の場合prototypeHTMLTextAreaElementクラスを使用する必要があります。

新しいcodepenの例

この貢献者彼のソリューションへのすべてのクレジット

React <= 15.5のみの古い回答

ではreact-dom ^15.6.0、あなたは使用することができsimulated通過するイベントのイベントオブジェクトにフラグを

var ev = new Event('input', { bubbles: true});
ev.simulated = true;
element.value = 'Something new';
element.dispatchEvent(ev);

例でコードペンを作りました

新しいフラグが必要な理由を理解するために、このコメントは非常に役に立ちました。

Reactの入力ロジックは、重複排除の変更イベントを重複排除するため、値ごとに2回以上発生しません。これは、ブラウザーのonChange / onInputイベントと、DOMノード値プロップ(JavaScriptを介して値を更新するとき)のセットの両方をリッスンします。これには、入力の値を手動で更新する場合にinput.value = 'foo'から{target:input}を使用してChangeEventをディスパッチするという副作用があります。Reactは、セットとイベントの両方を登録します。 '、それを重複イベントと見なし、それを飲み込みます。

「実際の」ブラウザで開始されたイベントはelement.valueのセットをトリガーしないため、これは通常のケースでは正常に機能します。トリガーするイベントにシミュレートされたフラグでタグを付けることにより、このロジックを密かに救済でき、反応すると常にイベントが発生します。 https://github.com/jquense/react/blob/9a93af4411a8e880bbc05392ccf2b195c97502d1/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js#L128


1
おかげでこれはreact-dom ^ 15.6.0で機能しました。しかし、React 16.0以降では、これは機能しなくなったようです。シミュレーションイベントを使用して変更イベントをトリガーする代わりのアイデアはありますか?
Qwerty 2017年

ヒントとなるポイントがあると思います:reactjs.org/blog/2017/09/26/react-v16.0.html#breaking-changesそれが何であるかについての考えはありますか?
Qwerty 2017年

@Qwerty私は私の答えを更新しました、それはあなたのために働くかもしれません
Grin

そして、どのようなonClickボタンをクリックしますか?この種のイベントをトリガーするにはどうすればよいですか?
ベンダー

@Bender 要素のネイティブクリックメソッドを呼び出すだけ
Grin

63

少なくともテキスト入力でonChangeは、入力イベントをリッスンしているようです:

var event = new Event('input', { bubbles: true });
element.dispatchEvent(event);

ブラウザのバージョンによって異なります。IE8は入力イベントをサポートしていません。また、テキストボックスから文字を削除しても、ie9は入力イベントを発生させません。developer.mozilla.org/en-US/docs/Web/Events/input
wallice

EventはIEをサポートしていませんhttps://developer.mozilla.org/en-US/docs/Web/API/Event/Event
Joyful

1
IE8 Reactでサポートされなくなりました。IE9の場合、のようなものを使用できる可能性がありますがvar event = document.createEvent('CustomEvent'); event.initCustomEvent('input', true, false, { });、私にはIE9 VMがありません。
マイケル

@Michael IE11でコードを試してみましたが、reactjs入力フィールドでは機能しません。通常のHTML入力では機能します。Edgeでも動作します。var evt = document.createEvent('CustomEvent'); evt.initCustomEvent('input', true, false, { }); document.getElementById('description').value = 'I changed description'; document.getElementById('description').dispatchEvent(evt);
ボドスコ

19

私はこの答えが少し遅れることを知っていますが、最近同様の問題に直面しました。ネストされたコンポーネントでイベントをトリガーする必要がありました。ラジオとチェックボックスタイプのウィジェット(チェックボックスやラジオボタンのように動作するdivでした)のリストがあり、誰かがツールボックスを閉じた場合は、チェックを外す必要がありました。

私はこれがベストプラクティスであるかどうかはわかりませんが、かなり簡単な解決策を見つけましたが、うまくいきます。

var event = new MouseEvent('click', {
 'view': window, 
 'bubbles': true, 
 'cancelable': false
});
var node = document.getElementById('nodeMyComponentsEventIsConnectedTo');
node.dispatchEvent(event);

これによりdomNodeでクリックイベントがトリガーされ、reactを介してアタッチされた私のハンドラーが実際に呼び出されたため、誰かが要素をクリックした場合に期待するように動作します。私はonChangeをテストしていませんが、動作するはずです。これが本当に古いバージョンのIEでどのように機能するかはわかりませんが、MouseEventは少なくともIE9以降でサポートされていると思います。

私のコンポーネントは非常に小さく(私はまだそれを学習しているので、アプリケーションの一部のみが反応に使用されました)、domノードへの参照を取得せずに別の方法で同じことを達成できるため、私は最終的にこれから私の特定のユースケースに移りました。

更新:

他の人がコメントで述べthis.refs.refnameたように、domノードへの参照を取得するために使用することをお勧めします。この場合、refnameはを介してコンポーネントにアタッチした参照です<MyComponent ref='refname' />


1
IDの代わりに、React.findDOMNode関数を使用できます。goo.gl/RqccrA
m93a

2
> IDの代わりに、React.findDOMNode関数を使用できます。またはref、エレメントにa を追加して使用しますthis.ref.refName.dispatchEvent
silkAdmin

フレームワークが変更された可能性が最も高いのは、this.ref s .refname
nclord

1
文字列参照は現在レガシーと見なされており、将来は非推奨となります。コールバック参照は、DOMノードを追跡するための推奨される方法です。
dzv3 2016年

9

ReactTestUtilsを使用してイベントをシミュレートできますが、これは単体テスト用に設計されています。

この場合はvalueLinkを使用せず、プラグインによって発生した変更イベントをリッスンし、それに応じて入力の状態を更新することをお勧めします。双方向バインディングは、何よりもデモとしてのutilsです。これらは、純粋な双方向バインディングがほとんどのアプリケーションに適切ではなく、通常、アプリでの相互作用を記述するためにより多くのアプリケーションロジックが必要であることを強調するためだけにアドオンに含まれています。


私は答えがそのようなものになるのではないかと心配しました(問題は、Reactが送信/受信ワークフローでは厳しすぎるということです。特に、ユーザー入力に反応できるようにするには、onChangeハンドラーを指定する必要があります。定型オーバーme.Inに私の場合、私は、関連する入力がjqueryのでトリガのonchangeイベントから来る間だけ、ルールに従うために、このハンドラを宣言する必要があり、実際の送信を拡張するアイデアを欠いリアクト/ユーザーとエンドポイントを受け取るコードを宣言した。
wallice

1
そして... ReactTestUtilsを使用したい場合...ReactTestUtils.Simulate.change(ReactDOM.findDOMNode(this.fieldRef))
colllin

8

これは、Grin / Dan Abramovからの回答を拡張して、複数の入力タイプにわたって機能します。Reactでテスト済み> = 15.5

const inputTypes = [
    window.HTMLInputElement,
    window.HTMLSelectElement,
    window.HTMLTextAreaElement,
];

export const triggerInputChange = (node, value = '') => {

    // only process the change on elements we know have a value setter in their constructor
    if ( inputTypes.indexOf(node.__proto__.constructor) >-1 ) {

        const setValue = Object.getOwnPropertyDescriptor(node.__proto__, 'value').set;
        const event = new Event('input', { bubbles: true });

        setValue.call(node, value);
        node.dispatchEvent(event);

    }

};

5
一部の要素では機能しません。イベントの「入力」ではなく「変更」が必要です。
スタイクス

3

任意の要素で変更イベントをトリガーすると、コンポーネント間の依存関係が作成され、その理由を推測することが困難になります。Reactの一方向のデータフローを使用することをお勧めします。

Reactの変更イベントをトリガーする単純なスニペットはありません。ロジックはChangeEventPlugin.jsに実装されており、さまざまな入力タイプとブラウザーに応じてさまざまなコードブランチがあります。さらに、実装の詳細はReactのバージョンによって異なります。

私はそれを行うreact-trigger-changeを構築しましたが、それはプロダクションの依存関係としてではなく、テストのために使用することを意図しています:

let node;
ReactDOM.render(
  <input
    onChange={() => console.log('changed')}
    ref={(input) => { node = input; }}
  />,
  mountNode
);

reactTriggerChange(node); // 'changed' is logged

CodePen


1

関数を使用してonchangeイベントを処理するので、次のようにすることができます。

class Form extends Component {
 constructor(props) {
  super(props);
  this.handlePasswordChange = this.handlePasswordChange.bind(this);
  this.state = { password: '' }
 }

 aForceChange() {
  // something happened and a passwordChange
  // needs to be triggered!!

  // simple, just call the onChange handler
  this.handlePasswordChange('my password');
 }

 handlePasswordChange(value) {
 // do something
 }

 render() {
  return (
   <input type="text" value={this.state.password} onChange={changeEvent => this.handlePasswordChange(changeEvent.target.value)} />
  );
 }
}

1

私はこれをReactのGithubの問題で見つけました:魅力のように機能します(v15.6.2)

これがテキスト入力に実装する方法です:

changeInputValue = newValue => {

    const e = new Event('input', { bubbles: true })
    const input = document.querySelector('input[name=' + this.props.name + ']')
    console.log('input', input)
    this.setNativeValue(input, newValue)
    input.dispatchEvent(e)
  }

  setNativeValue (element, value) {
    const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set
    const prototype = Object.getPrototypeOf(element)
    const prototypeValueSetter = Object.getOwnPropertyDescriptor(
      prototype,
      'value'
    ).set

    if (valueSetter && valueSetter !== prototypeValueSetter) {
      prototypeValueSetter.call(element, value)
    } else {
      valueSetter.call(element, value)
    }
  }

テキストボックスの値がsetNativeValueに渡した値と同じ場合、これは機能しません
Arun

0

の場合HTMLSelectElement、つまり<select>

var element = document.getElementById("element-id");
var trigger = Object.getOwnPropertyDescriptor(
  window.HTMLSelectElement.prototype,
  "value"
).set;
trigger.call(element, 4); // 4 is the select option's value we want to set
var event = new Event("change", { bubbles: true });
element.dispatchEvent(event);

-1

BackboneとReactを使用している場合は、次のいずれかをお勧めします。

どちらも、バックボーンモデルとコレクションをReactビューと統合するのに役立ちます。バックボーンビューと同じように、バックボーンイベントを使用できます。私は両方を試してみましたが、一方がミックスインであり、もう一方がに変更さReact.createClassれていることを除いて、ほとんど違いはありませんでしたReact.createBackboneClass


反応とバックボーンの間のこれらの「イベント駆動型」ブリッジに注意してください。最初のプラグインは、コンポーネントのマウントレベルに関係なくsetPropsを使用します。それは間違い。2つ目は、forceUpdateに大きく依存しており、コンポーネントに割り当てることができるモデルは1つだけであるということです。これは一般的な状況ではありません。さらに、リアクションコンポーネントを使用して複雑なUI全体でモデルを共有し、forceUpdateの原因となる不要な更新イベントのサブスクライブを忘れた場合は、再帰的なレンダリングに陥ることがあります。
ウォリス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.