本当に大きなReactフォームのパフォーマンスに苦労しています

プロジェクトの1つで、相互に依存する数十のブロックからフォームに出くわしました。いつものように、NDAのためにタスクについて詳細に話すことはできませんが、抽象的な(少しでも非現実的な)例を使用して、これらのフォームのパフォーマンスを「使いこなす」経験を説明しようとします。Final-formを使用したReactプロジェクトから得た結論をお伝えします。



画像


フォームを使用して、仲介者であるビザセンターを通じてシェンゲンビザの受領を処理しながら、新しいサンプルの外国のパスポートを取得できると想像してみてください。この例は、私たちの複雑さを示すのに十分な官僚的であるように思われます。



したがって、私たちのプロジェクトでは、特定のプロパティを持つ多くのブロックの形式に直面しています。



  • フィールドの中には、入力ボックス、複数選択、自動入力フィールドがあります。
  • ブロックはリンクされています。1つのブロックで内部パスポートのデータを指定する必要があり、そのすぐ下にビザ申請者のデータを含むブロックがあるとします。同時に、内部パスポートについてもビザセンターとの契約が交わされます。

  • – , , ( 10 , ) .
  • , , . , 10- , . : .
  • . . .


最終的なフォームは、垂直方向に約6000ピクセルを占めていました。これは、合計で約3〜4画面で、80を超える異なるフィールドです。このフォームと比較すると、StateServicesのアプリケーションはそれほど優れていないようです。質問の多さの点で最も近いのは、おそらくいくつかの大企業へのセキュリティサービスの質問か、ビデオコンテンツの好みについての退屈な意見調査です。



実際の問題では、大きなフォームはそれほど一般的ではありません。このようなフォームを「正面から」実装しようとすると(小さなフォームでの作業に慣れている方法と同様に)、結果を使用できなくなります。



主な問題は、適切なフィールドに各文字を入力すると、フォーム全体が再描画されることです。これには、特にモバイルデバイスでのパフォーマンスの問題が伴います。



また、エンドユーザーだけでなく、それを維持しなければならない開発者にとっても、フォームに対処することは困難です。特別な手順を踏まないと、コード内のフィールド間の関係を追跡するのが困難になります。ある場所での変更は、予測が難しい場合がある結果を伴います。



最終フォームの展開方法



プロジェクトではReactとTypeScriptを使用しました(タスクを完了すると、完全にTypeScriptに切り替えました)。したがって、フォームを実装するために、ReduxFormの作成者からReactFinal-formライブラリを取得しました。



プロジェクトの開始時に、フォームを個別のブロックに分割し、Final-formのドキュメントに記載されているアプローチを使用しました。悲しいかな、これは、フィールドの1つへの入力が、大きなフォーム全体に変化をもたらしたという事実につながりました。ライブラリは比較的新しいので、そこにあるドキュメントはまだ若いです。大きな金型の性能を向上させるための最良のレシピについては説明していません。私が理解しているように、プロジェクトでこれに直面している人はほとんどいません。また、小さなフォームの場合、コンポーネントを少し再描画してもパフォーマンスに影響はありません。



依存関係



私たちが直面しなければならなかった最初の曖昧さは、フィールド間の依存関係をどのように正確に実装するかでした。ドキュメントに従って厳密に作業すると、相互接続されたフィールドの数が多いため、大きくなりすぎたフォームの速度が低下し始めます。重要なのは依存関係です。ドキュメントは、フィールドの隣に外部フィールドへのサブスクリプションを置くことを提案しています。これが私たちのプロジェクトの様子です。フィールドの接続を担当するreact-final-form-listenersの適応バージョンは、コンポーネントと同じ場所に配置されていました。つまり、コンポーネントは隅々に配置されていました。依存関係を追跡することは困難でした。これはコードの量を膨らませました-コンポーネントは巨大でした。そして、すべてがゆっくりと機能しました。また、フォーム内の何かを変更するには、すべてのプロジェクトファイルで検索を使用するのに多くの時間を費やす必要がありました(プロジェクトには約600個のファイルがあり、そのうち100個以上がコンポーネントです)。



私たちは状況を改善するためにいくつかの試みをしました。



特定のブロックに必要なデータのみを選択する独自のセレクターを実装する必要がありました。



<Form onSubmit={this.handleSubmit} initialValues={initialValues}>
   {({values, error, ...other}) => (
      <>
      <Block1 data={selectDataForBlock1(values)}/>
      <Block2 data={selectDataForBlock2(values)}/>
      ...
      <BlockN data={selectDataForBlockN(values)}/>
      </>
   )}
</Form>


ご想像のとおり、私は自分で考え出す必要がありましたmemoize pick([field1, field2,...fieldn])



これらすべてに関連しPureComponent (React.memo, reselect)て、ブロックは、それらが依存するデータが変更された場合にのみ再描画されるという事実につながりました(はい、以前は使用されていなかったReselectライブラリをプロジェクトに導入し、ほとんどすべてのデータ要求を実行できるようにしました)。



その結果、フォームのすべての依存関係を記述する1つのリスナーに切り替えました。このアプローチのアイデアは、final-form-calculateプロジェクト(https://github.com/final-form/final-form-calculate)から採用し、ニーズに追加しました。



<Form
   onSubmit={this.handleSubmit}
   initialValues={initialValues}
   decorators={[withContextListenerDecorator]}
>

   export const listenerDecorator = (context: IContext) =>
   createDecorator(
      ...block1FieldListeners(context),
      ...block2FieldListeners(context),
      ...
   );

   export const block1FieldListeners = (context: any): IListener[] => [
      {
      field: 'block1Field',
      updates: (value: string, name: string) => {
         //    block1Field       ...
         return {
            block2Field1: block2Field1NewValue,
            block2Field2: block2Field2NewValue,
         };
      },
   },
];


その結果、フィールド間に必要な関係が得られました。さらに、データは1つの場所に保存され、より透過的に使用されます。さらに、これも重要であるため、サブスクリプションがトリガーされる順序がわかります。



検証



依存関係との類推により、検証を扱いました。



ほとんどすべての分野で、その人が正しい年齢を入力したかどうかを確認する必要がありました(たとえば、ドキュメントのセットが指定された年齢に対応しているかどうか)。すべてのフォームに散在する数十の異なるバリデーターから、1つのグローバルなバリデーターに切り替え、個別のブロックに分割しました。



  • パスポートデータの検証ツール、
  • トリップデータの検証ツール、
  • 以前に発行されたビザに関するデータについては、


これはパフォーマンスにほとんど影響しませんでしたが、さらなる開発を加速しました。これで、変更を加えるときに、個々のバリデーターで何が起こっているかを理解するためにファイル全体を調べる必要はありません。



コードの再利用



最初は1つの大きなフォームでアイデアを実行しましたが、時間の経過とともにプロジェクトは成長し、別のフォームが表示されました。当然、2番目の形式では、すべて同じアイデアを使用し、コードを再利用しました。



以前は、すべてのロジックを別々のモジュールに移動しました。新しいフォームに接続してみませんか?このようにして、コードの量と開発速度を大幅に削減しました。



同様に、新しいフォームには、古いフォームと共通のタイプ、定数、およびコンポーネントが含まれるようになりました。たとえば、一般的な権限があります。



合計の代わりに



問題は論理的です。フォームに別のライブラリを使用しなかったのは、このライブラリに問題があったためです。しかし、大きなフォームにはとにかく独自の問題があります。過去に私はFormikと一緒に仕事をしたことがあります。質問の解決策を見つけたという事実を考慮すると、Final-formの方が便利であることがわかりました。



全体として、これはフォームを操作するための優れたツールです。また、コードベースの開発に関するいくつかのルールとともに、開発を大幅に最適化するのに役立ちました。このすべての作業の追加のボーナスは、新しいチームメンバーをより早く最新の状態にする機能です。



ロジックを強調した後、特定のフィールドが何に依存するかがはるかに明確になりました。これについては、分析で3枚の要件を読む必要はありません。これらの条件下では、バグの監査には少なくとも2時間かかりますが、これらすべての改善までに2、3日かかる場合があります。この間ずっと、開発者はファントムエラーを探していましたが、それが何を示しているかからは明らかではありません。



記事の著者:Oleg Troshagin、Maxilekt。



PS私たちはルネットのいくつかのサイトで記事を公開しています。Maxilectからのすべての出版物やその他のニュースについて学ぶためにVKFBInstagram、またはTelegramチャネルのページを購読してください



All Articles