ホームページ > ウェブフロントエンド > jsチュートリアル > 実存的な React の質問と完璧なモーダル ダイアログ

実存的な React の質問と完璧なモーダル ダイアログ

Patricia Arquette
リリース: 2025-01-03 03:44:41
オリジナル
655 人が閲覧しました

Existential React questions and a perfect Modal Dialog

React で最も複雑なことは何だと思いますか?再レンダリングしますか?コンテクスト?ポータル?同時実行性?

いいえ。

React の最も難しい部分は、その周囲にある非 Reactすべてです。 「上に挙げたものはどのように機能するのですか?」という質問に対する答えです。やり方は簡単です。アルゴリズムに従ってメモを取るだけです。結果は決定的であり、常に同じになります (正しく追跡した場合)。それは単なる科学と事実です。

しかし、「コンポーネントを優れたものにするものは何ですか?」についてはどうでしょうか。または「… (何か) を実装する正しい方法は何ですか?」あるいは「ライブラリを使用するべきですか、それとも独自のソリューションを構築すべきですか?」ここでの事実上正しい唯一の答えは、「状況による」です。たまたまそれは最も役に立たないものです。

新しい記事用に、これよりも優れたものを見つけたいと思いました。しかし、この種の質問には単純な答えや普遍的な解決策があるはずがないため、この記事は「これが答えです。常にそうしてください」というよりは、私の思考プロセスを詳しく説明するものになりました。これからも役立つことを願っています。

それでは、機能をアイデアから運用可能なソリューションに移行するには何が必要でしょうか?簡単なモーダルダイアログを実装して見てみましょう。それについて何が複雑になるでしょうか? ?

ステップ 1: 最も単純な解決策から始める

「スパイク」とも呼ばれるものから始めましょう。これは、潜在的なソリューションを探索し、さらなる要件を収集するのに役立つ最も単純な実装です。モーダルダイアログを実装していることはわかっています。次のようなきれいなデザインがあると仮定しましょう:

Existential React questions and a perfect Modal Dialog

ダイアログは基本的に、ボタンのようなものがクリックされたときに表示される画面上の要素です。まさにそこから始めます。

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <div className="dialog">some content</div>
      ) : null}
    </>
  );
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

状態、クリックを待機するボタン、および状態が true のときに表示される将来のダイアログ。ダイアログには「閉じる」アクションも必要です:

<button
  className="close-button"
  onClick={() => setIsOpen(false)}
>
  Close
</button>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

これには「背景」もあります。これは、コンテンツをオーバーレイし、クリックされるとモーダルが消えるトリガーとなるクリック可能な半透明の div です。

<div
  className="backdrop"
  onClick={() => setIsOpen(false)}
></div>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

全員で:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <>
          <div
            className="backdrop"
            onClick={() => setIsOpen(false)}
          ></div>
          <div className="dialog">
            <button
              className="close-button"
              onClick={() => setIsOpen(false)}
            >
              Close
            </button>
          </div>
        </>
      ) : null}
    </>
  );
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

私は通常、早い段階で適切なスタイルを追加します。実装している機能が想定どおりの外観で画面に表示されるのを見ると、考えるのに役立ちます。さらに、機能のレイアウトを通知することもできます。これはまさにこのダイアログで行われることです。

すぐに背景の CSS を追加しましょう - これは特別なことではなく、画面全体を占める位置: 固定の div 上の半透明の背景だけです:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <div className="dialog">some content</div>
      ) : null}
    </>
  );
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

ダイアログは画面の中央に配置する必要があるため、少し興味深いものになります。もちろん、CSS でこれを実現する方法は 1001 通りありますが、私のお気に入りでおそらく最も簡単な方法は次のとおりです:

<button
  className="close-button"
  onClick={() => setIsOpen(false)}
>
  Close
</button>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

レイアウトの制約から逃れるために「固定」位置を使用し、左と上に 50% を追加して div を中央寄りに移動し、50% だけ元に戻します。左と上は画面に対して相対的に計算され、変換は div 自体の幅/高さに相対的に行われるため、結果として画面の幅や幅に関係なく中央に表示されます。

このステップの CSS の最後の部分は、ダイアログ自体と「閉じる」ボタンを適切にスタイル設定することです。ここではコピー&ペーストしません。実際のスタイルはそれほど重要ではありません。例を見てください:

ステップ 2: 立ち止まって質問し、考える

機能の大まかな実装ができたので、今度はそれを「実際」にします。そのためには、ここで正確に何を、誰のために解決しようとしているのかを詳細に理解する必要があります。技術的に言えば、何かをコーディングするを理解する必要があるため、多くの場合、このステップはステップ 1である必要があります。

このダイアログは、できるだけ早く実装する必要があり、投資家に一度見せたら二度と使用しないプロトタイプの一部ですか?それとも、npm およびオープンソースで公開する予定の汎用ライブラリの一部なのでしょうか?それとも、5,000 人規模の組織が使用するデザイン システムの一部なのでしょうか?それとも、それは 3 人の小さなチームのための内部ツールの一部であり、他には何もないのでしょうか?それとも、TikTok などで働いていて、このダイアログはモバイルでのみ利用できる Web アプリの一部になるのでしょうか?それとも、政府専用のアプリを作成する代理店で働いているのでしょうか?

これらの質問に答えることで、コーディングに関して次に何をすべきかの方向性が決まります。

一度使ってみるプロトタイプなら、もう十分かもしれません。

ライブラリの一部としてオープンソース化する場合は、世界中の開発者が使用して理解できる非常に優れた汎用 API、多くのテスト、優れたドキュメントが必要です。

5,000 人規模の組織の設計システムの一部であるダイアログは、組織の設計ガイドラインに準拠する必要があり、リポジトリに取り込まれる外部依存関係が制限される場合があります。したがって、npm install new-fancy-tool を実行するのではなく、多くのことを最初から実装する必要があるかもしれません。

政府のために構築する機関の対話は、おそらく世界で最もアクセスしやすく、規制に準拠した対話である必要があります。そうしないと、政府機関は政府との契約を失い、破産する可能性があります。

などなど。

この記事では、このダイアログが、世界中から毎日何千人ものユーザーが集まる既存の大規模商用 Web サイトの、現在進行中の新たな再設計の一部であると仮定します。再設計が進行中であるため、ダイアログを含む唯一のデザインは次のとおりです:

Existential React questions and a perfect Modal Dialog

残りは後ほど、デザイナーたちは忙殺されています。また、私は単一のプロジェクトのために雇われた外部請負業者ではなく、再デザインを行って今後のウェブサイトの保守を行う常設チームの一員です。

この場合、この写真だけがあり、会社の目標について知っていれば、合理的な仮定を立てて対話の 90% を実行するのに十分な情報が得られます。残りの 10% は後で微調整できます。

これらは、上記の情報に基づいて私が立てることができる仮定です:

  • 既存の Web サイトには毎日世界中から何千人ものユーザーがアクセスするため、少なくともダイアログが大画面とモバイル画面の両方、およびさまざまなブラウザーで動作することを確認する必要があります。理想的には、確実に既存の分析をチェックする必要がありますが、これはかなり安全な方法です。

  • 複数の開発者がこのためのコードを作成しており、そのコードは存続します。この Web サイトは大規模で、すでに何千人ものユーザーがいます。それは投資家にとって簡単なプロトタイプではありません。したがって、コードが読みやすく、API が意味をなし、使いやすく保守しやすく、明らかな不法行為がないことを確認する必要があります。

  • その会社は自社のイメージと Web サイトの品質を重視しています。そうでない場合、そもそもなぜ再デザインを行うのでしょうか? (ここでは肯定的な意図があると仮定しましょう?)。つまり、一定レベルの品質が期待されており、たとえそれが現在の設計に含まれていないとしても、一般的なシナリオやエッジケースを事前に考えて予測する必要があります。

  • 多くのユーザーは、Web サイトとの対話にマウスのみを使用するユーザーが全員ではないことを意味していると思われます。このダイアログは、キーボード操作や、場合によってはスクリーン リーダーなどの支援技術によっても利用できる必要があります。

  • 大規模な既存のコードベース (再設計です、覚えておいてください!) は、この機能にもたらすことができる外部依存関係に制限がある可能性があることを意味します。特に大規模で古いコードベースでは、外部依存関係にはコストがかかります。この記事では、外部ライブラリを使用できると仮定しますが、これには十分な根拠が必要です。

  • 最後に、さらに多くのデザインが登場するため、デザインとユーザーの観点からそれがどのような方向に進むかを予測し、コードが早い段階でそれを処理できることを確認する必要があります。

ステップ 3: モーダル ダイアログ API を確立する

要件が分かり、妥当な推測ができたので、実際のダイアログ コンポーネントを作成できます。まず、このコードから:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <div className="dialog">some content</div>
      ) : null}
    </>
  );
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

ダイアログ部分を再利用可能なコンポーネントに抽出する必要があります。実装すべきダイアログベースの機能がたくさんあります。

<button
  className="close-button"
  onClick={() => setIsOpen(false)}
>
  Close
</button>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

ダイアログには onClose プロパティがあり、「閉じる」ボタンまたは背景がクリックされたときに親コンポーネントに通知します。親コンポーネントは状態を保持し、次のようにダイアログをレンダリングします。

<div
  className="backdrop"
  onClick={() => setIsOpen(false)}
></div>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

それでは、デザインをもう一度見て、ダイアログについてもう少し考えてみましょう:

Existential React questions and a perfect Modal Dialog

ダイアログにはアクション ボタンを備えた「フッター」部分が明らかに存在します。おそらく、これらのボタンには、1 つ、2 つ、3 つ、左揃え、右揃え、間にスペースを入れるなど、たくさんのバリエーションがあるでしょう。また、このダイアログには ヘッダー がありませんが、ヘッダーのあるダイアログは非常に一般的なパターンです。ここには、確認テキストからフォーム、インタラクティブなエクスペリエンス、誰も読まない非常に長い「利用規約」のスクロール可能なテキストまで、完全にランダムなコンテンツを含む コンテンツ エリアが必ず存在します。

最後に、サイズです。デザイン内のダイアログは非常に小さく、単なる確認ダイアログです。大きなフォームや長いテキストはそこには収まりません。したがって、ステップ 2 で収集した情報を考慮すると、ダイアログのサイズを変更する必要があると考えて間違いありません。現時点では、デザイナーがデザイン ガイドラインを持っている可能性が高いことを考慮すると、ダイアログには「小」、「中」、「大」の 3 つのバリエーションがあると想定できます。

これはすべて、ModalDialog に props が必要であることを意味します。フッターとヘッダーは ReactNode を受け入れる通常の props になり、サイズは単なる文字列の結合になり、メイン部分としてコンテンツ領域が入ります。子供たち:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <>
          <div
            className="backdrop"
            onClick={() => setIsOpen(false)}
          ></div>
          <div className="dialog">
            <button
              className="close-button"
              onClick={() => setIsOpen(false)}
            >
              Close
            </button>
          </div>
        </>
      ) : null}
    </>
  );
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

プロパティから得られる追加の className を使用してダイアログのサイズを制御します。ただし、実際には、リポジトリで使用されるスタイリング ソリューションに大きく依存します。

しかし、このバリアントでは、ダイアログが非常に柔軟であり、ほとんどすべてのものをどこでも使用できます。たとえば、フッターにはほとんどの場合、ボタンが 1 つか 2 つあるだけで、それ以上は何も期待できません。そして、これらのボタンは Web サイト全体のどこにでも一貫して配置される必要があります。それらを整列させるラッパーが必要です:

.backdrop {
  background: rgba(0, 0, 0, 0.3);
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
ログイン後にコピー
ログイン後にコピー

コンテンツについても同様です。少なくとも、コンテンツの周囲にある程度のパディングとスクロール機能が必要です。また、ヘッダーにはテキストのスタイルが必要になる場合があります。したがって、レイアウトは次のようになります:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <div className="dialog">some content</div>
      ) : null}
    </>
  );
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

しかし、残念ながら、それを保証することはできません。ある時点で、ボタン以外の何かをフッターに追加したいと思う人がいる可能性が非常に高いです。または、一部のダイアログには、販売されている背景にヘッダーが必要になる場合があります。または、コンテンツにパディングが必要ない場合もあります。

私がここで言いたいのは、いつかヘッダー/コンテンツ/フッター部分のスタイルを設定できるようにする必要があるということです。そしておそらく予想よりも早いでしょう。

もちろん、その構成を props とともに渡し、headerClassName、contentClassName、footerClassName などの props を持たせることもできます。場合によっては、実際には大丈夫かもしれません。しかし、素晴らしい再デザインのための素晴らしいダイアログのようなものについては、もっと良くできるはずです。

この問題を解決する非常に優れた方法は、次のようにヘッダー/コンテンツ/フッターを独自のコンポーネントに抽出することです。

<button
  className="close-button"
  onClick={() => setIsOpen(false)}
>
  Close
</button>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

そして ModalDialog コードをラッパーなしのコードに戻します:

<div
  className="backdrop"
  onClick={() => setIsOpen(false)}
></div>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

こうすることで、親アプリでダイアログ パーツのデフォルトのデザインを使用したい場合は、これらの小さなコンポーネントを使用します。

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <>
          <div
            className="backdrop"
            onClick={() => setIsOpen(false)}
          ></div>
          <div className="dialog">
            <button
              className="close-button"
              onClick={() => setIsOpen(false)}
            >
              Close
            </button>
          </div>
        </>
      ) : null}
    </>
  );
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

そして、完全にカスタムなものが必要な場合は、ModalDialog 自体をいじらずに、独自のカスタム スタイルを持つ新しいコンポーネントを実装します。

.backdrop {
  background: rgba(0, 0, 0, 0.3);
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
ログイン後にコピー
ログイン後にコピー

さらに言えば、ヘッダーとフッターのプロップももう必要ありません。 DialogHeader と DialogFooter を子に渡すだけで、ModalDialog をさらに簡素化し、どこでも一貫したデザインを保ちながら、同じレベルの柔軟性を備えたさらに優れた API を実現できます。

親コンポーネントは次のようになります:

.dialog {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}
ログイン後にコピー

ダイアログの API は次のようになります:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <>
          <div
            className="backdrop"
            onClick={() => setIsOpen(false)}
          ></div>
          <div className="dialog">
            <button
              className="close-button"
              onClick={() => setIsOpen(false)}
            >
              Close
            </button>
          </div>
        </>
      ) : null}
    </>
  );
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

今のところかなり満足しています。デザインに必要なあらゆる方法で拡張できる十分な柔軟性を備えていますが、アプリ全体に一貫した UI を簡単に実装できるほど明確で賢明でもあります。

これが実際に試してみるサンプルです:

ステップ 4: パフォーマンスと再レンダリング

Modal の API が十分に適切な形になったので、私が実装した明らかなフットガンに取り組むときが来ました。私の記事を十分に読んだ人なら、おそらく「何をやってるの? 再レンダリングだ!!」と大声で叫んだことがあるでしょう。最後の10分間?そしてもちろん、あなたは正しいです:

const ModalDialog = ({ onClose }) => {
  return (
    <>
      <div className="backdrop" onClick={onClose}></div>
      <div className="dialog">
        <button className="close-button" onClick={onClose}>
          Close
        </button>
      </div>
    </>
  );
};
ログイン後にコピー

ここの Page コンポーネントには状態があります。モーダルが開いたり閉じたりするたびに状態が変化し、コンポーネント全体とその内部のすべてが再レンダリングされます。確かに「時期尚早の最適化は諸悪の根源」であり、実際に測定する前にパフォーマンスを最適化しないでください。この場合、従来の通念を無視しても問題ありません。

理由は 2 つあります。まず、アプリ全体に多数のモーダルが散在することは事実としてわかっています。これは、誰も使用しない 1 回限りの隠された機能ではありません。したがって、誰かがこのような API を使用して、あるべきではない場所に状態を配置する可能性は非常に高いです。第 2 に、そもそも再レンダリングの問題の発生を防ぐのに、それほど時間も労力もかかりません。たった 1 分間の作業で、ここでのパフォーマンスについてはまったく考える必要がなくなります。

私たちがする必要があるのは、状態をカプセル化し、「非制御コンポーネント」の概念を導入することだけです。

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <div className="dialog">some content</div>
      ) : null}
    </>
  );
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

BaseModalDialog は以前のダイアログとまったく同じですが、名前を変更しただけです。

次に、ダイアログをトリガーするコンポーネントをトリガー プロップとして渡します。

<button
  className="close-button"
  onClick={() => setIsOpen(false)}
>
  Close
</button>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

ページ コンポーネントは次のようになります:

<div
  className="backdrop"
  onClick={() => setIsOpen(false)}
></div>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

ページ内に状態が存在することはなくなり、潜在的に危険な再レンダリングもなくなりました。

ほとんどの場合、ダイアログを表示するにはユーザーが何かをクリックする必要があるため、このような API はユースケースの 95% をカバーするはずです。まれに、ショートカット上やオンボーディングの一部としてダイアログを独立して表示する必要がある場合でも、BaseModalDialog を使用して状態を手動で処理できます。

ステップ 5: 特殊なケースとアクセシビリティへの対処

ModalDialog コンポーネントの API は React の観点から見ると非常に堅牢ですが、仕事はまだ終わっていません。ステップ 2 で収集した必需品を考慮すると、さらにいくつかの問題を修正する必要があります。

問題 1: トリガーを追加のスパンにラップしています。場合によっては、ページのレイアウトが崩れる可能性があります。何とかしてラッパーを取り除く必要があります。

問題 2: 新しいスタッキング コンテキストを作成する要素内でダイアログをレンダリングすると、一部の要素の下にモーダルが表示されます。現在のようにレイアウト内で直接レンダリングするのではなく、ポータル内でレンダリングする必要があります。

問題 3: 現時点では、キーボードへのアクセスが非常に悪いです。適切に実装されたモーダル ダイアログが開くと、フォーカスが内部に移動するはずです。閉じると、ダイアログをトリガーした要素にフォーカスが戻ります。ダイアログが開いているとき、フォーカスは内部に「トラップ」され、外部の要素はフォーカス可能であってはなりません。 ESC ボタンを押すとダイアログが閉じます。現時点では、これはどれも実装されていません。

問題 1 と 2 は少し面倒ですが、比較的早く解決できます。ただし、問題 3 を手動で行うのは非常に面倒です。さらに、これは確実に解決された問題です。どこにいても、すべてのダイアログにこの機能が必要です。

「自分でやるには大変な苦労」「確実に解決された問題のように見える」という組み合わせがあれば、私は既存のライブラリを探すことになります。

すでに行ったすべての事前作業を考慮すると、適切なものを選択するのは簡単です。

Ant Design やマテリアル UI などの既存の UI コンポーネント ライブラリを選択し、そこからダイアログを使用することもできます。しかし、再設計でそれらが使用されない場合、必要なものに合わせて設計を調整することは、解決するよりもさらに多くの苦痛をもたらすことになります。したがって、この件に関しては即座にノーです。

Radix や React Aria などの「ヘッドレス」UI ライブラリのいずれかを使用できます。これらは状態やトリガーなどの機能とすべてのアクセシビリティを実装しますが、設計は消費者に委ねられます。 API を確認しながら、ダイアログを手動でトリガーしたい場合に本当に必要な場合 (実際にトリガーする必要がある場合)、ダイアログの状態を制御できるかどうかを再確認する必要があります。

何らかの理由でヘッドレス ライブラリを使用できない場合は、少なくともフォーカス トラップ機能を処理するライブラリを使用しようとします。

記事のために、必要なライブラリを何でも持ち込めると仮定しましょう。この場合、Radix を使用します。これは非常に使いやすく、ダイアログの API は既に実装したものと非常によく似ているため、リファクタリングは簡単です。

ダイアログ自体の API を少し変更する必要があります:

export default function Page() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <button onClick={() => setIsOpen(true)}>
        Click me
      </button>
      {isOpen ? (
        <div className="dialog">some content</div>
      ) : null}
    </>
  );
}
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

以前とほぼ同じです。ただし、どこでも div の代わりに、Radix プリミティブを使用します。

制御されていないダイアログの使用法はまったく変わりません:

<button
  className="close-button"
  onClick={() => setIsOpen(false)}
>
  Close
</button>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

制御されたダイアログはわずかに変更されます - 条件付きレンダリングの代わりに props を渡す必要があります:

<div
  className="backdrop"
  onClick={() => setIsOpen(false)}
></div>
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー
ログイン後にコピー

以下の例を確認し、キーボードを使用して操作してみてください。すべてが必要なとおりに機能します。なんて素晴らしいことでしょう?

おまけに、Radix はポータルの問題も処理し、トリガーをスパンでラップしません。もう解決すべきエッジケースはないので、最後のステップに進むことができます。

ステップ6: 最終磨き

この機能はまだ完成していません! ?ダイアログの見た目も感触もかなり安定したものになったので、現段階では実装に大きな変更を加えるつもりはありません。しかし、私が解決しているユースケースにとって「完璧な」ダイアログとみなされるには、まだいくつかの点が必要です。

1 つ: デザイナーがまだ行っていない場合、最初に私に依頼するのは、ダイアログが開くときに微妙なアニメーションを追加することです。それを予測して、React でアニメーションを実行する方法を覚えておく必要があります。

Two: 小さな画面でも適切に見えるように、ダイアログに max-width と max-height を追加する必要があります。そして、非常に大きな画面でどのように見えるか考えてみましょう。

Three: モバイル上でダイアログがどのように動作するかについてデザイナーと話し合う必要があります。おそらく、ダイアログのサイズに関係なく、画面の大部分を占めるスライドイン パネルにするよう依頼されるでしょう。

4 つ: 少なくとも DialogTitle コンポーネントと DialogDescription コンポーネントを導入する必要があります。Radix はアクセシビリティ目的でそれらを使用するよう求めます。

5: テスト!このダイアログは存続し、他の人によって維持されるため、この場合テストはほぼ必須です。

そしておそらく私が今忘れているその他の小さなことはたくさんありますが、後で取り上げます。ダイアログのコンテンツの実際のデザインを実装することは言うまでもありません。

さらにいくつかの考え

上記の「ダイアログ」を「SomeNewFeature」に置き換えると、ほぼすべての新しいものを実装するために私が使用するアルゴリズムがこれになります。

ソリューションを迅速に「スパイク」→機能の要件を収集→機能させる→パフォーマンスを向上させる→完成させる→完璧にする

これまでに何百回も実装してきた実際のダイアログのようなものについては、頭の中で最初のステップを 10 秒以内に実行し、すぐにステップ 2 から始めます。

非常に複雑で未知のものの場合、ステップ 1 が長くなる可能性があり、すぐにさまざまなソリューションやライブラリを探索する必要があります。

正確には未知ではなく、単に「実行する必要がある通常の機能」である場合は、探索するものが何もない可能性があるため、ステップ 1 をスキップする可能性があります。

特に「アジャイル」環境では、要件が段階的に提供され、頻繁に変更される直線というよりスパイラルになることが非常に多く、私たちは定期的に最初の 2 つのステップに戻ります。


この種の記事がお役に立てば幸いです! ??このようなコンテンツをもっと増やしたい場合、または通常の「物事の仕組み」に関する内容を希望する場合は、お知らせください。

そして、このプロセスが皆さんの頭の中でどう違うのかを聞くのを楽しみにしています?


元々は https://www.developerway.com で公開されました。ウェブサイトにはこのような記事が他にもありますか?

Advanced React ブックを読んで、React の知識を次のレベルに引き上げてください。

ニュースレターを購読したり、LinkedIn で接続したり、Twitter でフォローしたりすると、次の記事が公開されたらすぐに通知を受け取ることができます。


ところで、最後にもう 1 つ: 新しいプロジェクトをすぐに開始する予定で、デザイナーがいない場合と、説明したようにデザイン エクスペリエンスを磨く時間がない場合 - 私は最近、新しいプロジェクトの実装に何時間も費やしました。この場合の UI コンポーネントのライブラリ。コピー&ペースト可能なコンポーネントと共通パターン、Radix と Tailwind、ダーク モード、アクセシビリティ、すぐに使えるモバイル サポートが備わっています。上記の完璧なモーダル ダイアログが含まれています。 ?

試してみましょう: https://www.buckets-ui.com/

Existential React questions and a perfect Modal Dialog

以上が実存的な React の質問と完璧なモーダル ダイアログの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート