React 初心者がよく尋ねる質問は、なぜ双方向データ バインディングが組み込み機能ではないのか、というものです。通常の回答では、双方向データ バインディングはパフォーマンス上の理由から必ずしも望ましいわけではないという考えとともに、一方向データ フローの説明が含まれています。これは、私がより詳しく理解したい 2 番目のポイントです。
私は現在、フォームライブラリに取り組んでいます。アポロリンクステート(Apollo の新しいクライアント側状態管理ツール)。概念は redux-form と非常に似ていますが、状態マネージャーとして redux ではなく apollo-link-state を使用する点が異なります。(フォームの状態はドメイン エンティティの状態とは別に保存されますが、オプションでエンティティを使用してフォームの初期状態を設定することもできます。)
ユーザーがフォームに変更を加えると、ライブラリはonChange
ハンドラーを介してストアを即座に更新します。プログラマーがパフォーマンスを懸念している場合に備えて、個々のフィールドでその動作をオプトアウトできるようにすることを考えていましたが、これが実際にパフォーマンスの問題になるのはいつなのか疑問に思い始めました。ブラウザはoninput
どのような場合でもイベントを発生させるので、私が考えられる唯一のパフォーマンス上の考慮事項は、ユーザーが入力したときにストアが更新されるかどうかです。確かに、 を呼び出すのではなく、ミューテーションを実行するという追加のオーバーヘッドがありますが、それはsetState()
基本的にいくつかの追加関数呼び出しに相当します。また、apollo を使用せず、グローバルストアを直接更新する関数を呼び出すだけだと仮定しましょう。その場合、パフォーマンス上の考慮事項は何でしょうか?
私の考えでは、フォームが、ユーザーが 1 つのフィールドに入力するとすぐにフォームの状態を更新することをサポートするのであれば、すべてのフィールドでそのようにするほうがよいと思います。ユーザーは一度に 1 つのフィールドにしか入力できません。また、一部のフィールドではページが速くなる (おそらく無視できる程度) 一方で、他のフィールドでは遅くなるというメリットは見当たりません。さらに、私のライブラリでは、コンシューマーが任意の入力コンポーネントを使用できるため、プログラマーが状態の更新を減らしたいだけであれば、React のonChange
イベントをデバウンスするコンポーネントを作成するか、代わりにブラウザー独自のイベントchange
またはblur
イベントを使用するだけで済みます。
何か見落としているのでしょうか? ライブラリのユーザーがフォームを送信するまで特定のフィールドの変更を無視したい理由は他にもあるのでしょうか? あるいは、フォーム全体の変更を (送信するまで) 無視する方が便利なオプションでしょうか?
以下は、私の現在のアプローチの背後にある基本概念の基本的な(大幅に簡略化された)図です。
// defined in a globally-accessible module
const formState = {
// This somehow causes any dependent form components to re-render
// when state changes
update(formName, updatedState) {
...
}
}
export default formState
...
// UserForm.js:
export default class UserForm extends PureComponent {
componentDidMount() {
formState.userForm = {
firstName: '',
lastName: '',
}
}
handleChange(e) {
const { target } = e
formState.update('userForm', { [target.name]: target.value })
}
//...
render() {
const { userForm } = formState
return (
<form onSubmit={this.handleSubmit}>
<label for="name">Name</label>
<input id="name" type="text" onChange={this.handleChange} value={userForm.name} />
<label for="email">Email</label>
<input id="email" type="email" onChange={this.handleChange} value={userForm.email} />
</form>
)
}
}
最後に、完全性のために、これにはAPI設計上の考慮事項もいくつかあることを述べておきます。個々の入力コンポーネントは、次のようにすればもう少しシンプルな設計になるかもしれません。ない自動双方向バインディングをオプトアウトするオプションを提供します。ご興味があれば詳細を投稿できます。
ベストアンサー1
双方向データバインディングの影響
質問の最初の部分から始めると、React が双方向データ バインディングを採用しない主な理由は 2 つあります。
- あ真実の唯一の情報源Reactアプリのデータ変更のため、バグの可能性が低くなり、デバッグが容易になります。
- パフォーマンス上の利点
Reactでは、異なる子コンポーネント間で状態を共有することができます。国家の向上共通の親コンポーネントに共有状態が更新されると、すべての子コンポーネントが自分自身を更新できます。ここフォームに関連するドキュメントの良い例です。
パフォーマンス上の利点について言えば、他のコンテキスト (AngularJS など) での双方向データ バインディングは、さまざまな要素を監視するウォッチャーによって機能します。要素の数が少ない場合は、これは簡単 (React の一方向データ フローよりもコードが少ない) に思えますが、UI コンポーネント/要素の数が増えると、ウォッチャーの数も増えます。この場合、1 つの変更で、同期を維持するために多くのウォッチャーが起動します。これにより、パフォーマンスが少し低下します。React の場合は、一方向データ フローしかないため、どのコンポーネントを更新する必要があるかを判断するのが簡単です。
状態更新の処理
質問の 2 番目の部分に移りますが、状態ライブラリはフォーム コンポーネントにデータを提供し、状態の変化に応じて依存コンポーネントが更新されます。すばらしいですね。私の考えは次のとおりです。
プログラマーがパフォーマンスを懸念している場合に備えて、個々のフィールドでその動作をオプトアウトできるようにすることを考えていましたが、これが実際のパフォーマンスの問題になるのはいつになるのか疑問に思い始めました。
ストアの更新自体はかなり速く行われます。JavaScript は非常に高速に実行されますが、ボトルネックの原因となるのは多くの場合 DOM の更新です。したがって、同じページに何百もの依存フォーム要素があり、それらすべてが更新されない限り、問題はありません。
また、apollo を使用せず、グローバル ストアを直接更新する関数を呼び出すだけだと仮定すると、パフォーマンスの考慮事項はどうなるでしょうか?
大きな違いはないと思います。
私の考えでは、ユーザーが 1 つのフィールドに入力するとすぐにフォームの状態が更新されるようにフォームがサポートされるのであれば、すべてのフィールドでそのようにしたほうがよいと思います。ユーザーは一度に 1 つのフィールドにしか入力できません。また、一部のフィールドではページが時々速くなり (おそらく無視できる程度)、他のフィールドでは時々遅くなるというメリットは見当たりません。
これに同意します。
私のライブラリでは、消費者は必要な入力コンポーネントを自由に使用できるため、プログラマーが状態の更新を減らしたいだけであれば、React の onChange イベントをデバウンスするコンポーネントを作成するか、代わりにブラウザー独自の change イベントまたは blur イベントを使用するだけで済みます。
ほとんどのユースケースは単純な で解決できると思いますinput
。繰り返しますが、ここで状態の更新が少なくなってもパフォーマンス上の利点は見られません。たとえば、入力に対して API 呼び出しを実行している場合 (ユーザーが入力を停止するまで待機したい場合)、デバウンスが役立つ場合があります。
ライブラリのユーザーがフォームを送信するまで特定のフィールドの変更を無視したい理由は他にありますか? または、フォーム全体の変更を (送信するまで) 無視する方が便利なオプションでしょうか?
特定のフィールドの変更を無視したり、送信するまで待ったりすることにメリットはないと思います。一方、フォームを使用する際によく見かけるユースケースは、データの検証です。たとえば、
- ユーザーがパスワードを作成するときにフィードバックを提供する
- メールが有効かどうかを確認する
- ユーザー名が有効かどうかなどを確認するために API 呼び出しを実行します。
これらのケースでは、ユーザーが入力しているときに状態を更新する必要があります。
要約
ユーザーが入力しているときに状態を更新しても問題ないはずです。それでもパフォーマンスが気になる場合は、コンポーネントのプロファイルを作成するボトルネックがある場合はそれを切り離すためです:)