1. ホーム
  2. ジャバスクリプト

[解決済み】Reduxで非同期アクションを実行するモーダルダイアログを表示するにはどうすればよいですか?

2022-04-17 06:19:14

質問

ある状況下で確認ダイアログを表示する必要があるアプリを作成しています。

例えば、何かを削除したい場合、次のようなアクションをディスパッチします。 deleteSomething(id) そのため、いくつかのreducerがそのイベントをキャッチし、それを表示するためにダイアログreducerを埋めることになります。

私の疑問は、このダイアログが送信されるときです。

  • このコンポーネントは、最初にディスパッチされたアクションに応じて、どのように適切なアクションをディスパッチすることができますか?
  • このロジックは、アクションクリエイターが処理すべきですか?
  • reducerの内部にアクションを追加することは可能か?

を編集します。

を追加し、より分かりやすくしました。

deleteThingA(id) => show dialog with Questions => deleteThingARemotely(id)

createThingB(id) => Show dialog with Questions => createThingBRemotely(id)

そこで、ダイアログコンポーネントを再利用しようと思っています。ダイアログの表示/非表示は、リデューサで簡単に行えるので問題ではありません。私が指定しようとしているのは、左側でフローを開始するアクションに従って、右側からアクションをディスパッチする方法です。

どのように解決するのですか?

更新情報 : React 16.0からポータルが導入されました。 ReactDOM.createPortal リンク

更新情報 React の次のバージョン(繊維:おそらく 16 または 17)には、ポータルを作成するメソッドが含まれる予定です。 ReactDOM.unstable_createPortal() リンク


ポータルサイトの利用

Dan Abramov回答最初の部分は良いのですが、多くの定型文が含まれています。彼が言ったように、ポータルを使うこともできます。そのアイデアについて、もう少し詳しく説明します。

ポータルの利点は、ポップアップとボタンがReactツリーに非常に近いまま、propsを使用して非常にシンプルな親子通信を行うことです。ポータルで非同期アクションを簡単に処理したり、親にポータルをカスタマイズさせたりすることが可能です。

ポータルとは何ですか?

ポータルを使用すると document.body Reactツリー内に深くネストされた要素です。

例えば、以下のようなReactツリーをbodyにレンダリングするということです。

<div className="layout">
  <div className="outside-portal">
    <Portal>
      <div className="inside-portal">
        PortalContent
      </div>
    </Portal>
  </div>
</div>

そして、出力として得られます。

<body>
  <div class="layout">
    <div class="outside-portal">
    </div>
  </div>
  <div class="inside-portal">
    PortalContent
  </div>
</body>

inside-portal ノードの内部で翻訳されています。 <body> を、通常の深いネストの場所ではなく、"left" と呼びます。

ポータルを使用する場合

ポータルは、ポップアップ、ドロップダウン、サジェスト、ホットスポットなど、既存のReactコンポーネントの上に置くべき要素を表示する場合に特に便利です。

ポータルを使用する理由

z-indexの問題はもうない へのレンダリングが可能です。 <body> . ポップアップやドロップダウンを表示する場合、z-indexの問題と戦う必要がないのであれば、これは本当にいいアイデアです。ポータル要素は、以下のように追加されます。 document.body をマウント順に並べるということです。 z-index の場合、デフォルトの動作は、ポータルをマウント順に重ねることになります。実際には、これは他のポップアップの中からポップアップを安全に開くことができ、2番目のポップアップが最初のポップアップの上に表示されることを保証することを意味します。 z-index .

実践編

最もシンプル:ローカルなReactの状態を使用する。 もし、単純な削除確認ポップアップのために、Reduxのボイラープレートを持つ価値がないと思うなら、ポータルを使うことができ、コードを大幅に単純化することができます。このようなユースケースでは、インタラクションは非常にローカルで、実際にはかなり細かいところまで実装されています。ホットロード、タイムトラベリング、アクションログなど、Reduxがもたらすすべての利点を本当に気にする必要がありますか?個人的には、私はそうではなく、このような場合にはローカルステートを使用します。コードは次のようにシンプルになります。

class DeleteButton extends React.Component {
  static propTypes = {
    onDelete: PropTypes.func.isRequired,
  };

  state = { confirmationPopup: false };

  open = () => {
    this.setState({ confirmationPopup: true });
  };

  close = () => {
    this.setState({ confirmationPopup: false });
  };

  render() {
    return (
      <div className="delete-button">
        <div onClick={() => this.open()}>Delete</div>
        {this.state.confirmationPopup && (
          <Portal>
            <DeleteConfirmationPopup
              onCancel={() => this.close()}
              onConfirm={() => {
                this.close();
                this.props.onDelete();
              }}
            />
          </Portal>
        )}
      </div>
    );
  }
}

シンプル:まだReduxの状態を使用することができます を使用することができます。 connect を使用するかどうかを選択するために DeleteConfirmationPopup を表示するかどうかを決定します。ポータルはReactツリーに深くネストしたままなので、親がポータルにpropを渡すことができ、このポータルの動作をカスタマイズするのは非常に簡単です。ポータルを使用しない場合、通常、ポップアップをReactツリーの最上位でレンダリングする必要があります。 z-index のような理由で、通常、「ユースケースに応じて構築した汎用的な DeleteConfirmationPopup をどのようにカスタマイズするか」ということを考えなければなりません。例えば、ネストしたconfirm/cancelアクションを含むアクションをディスパッチしたり、翻訳バンドルキーや、さらに悪いことにレンダー関数(またはシリアル化できない何か)をディスパッチするような、この問題に対する非常に陳腐な解決策が見つかることがあります。ポータルではそのようなことをする必要はなく、通常のプロップを渡すだけでよいのです。 DeleteConfirmationPopup の単なる子です。 DeleteButton

結論

ポータルは、コードを簡素化するのにとても便利です。私はもうこれなしではやっていけません。

ポータルの実装は、次のような他の便利な機能にも役立ちますので、注意してください。

  • アクセシビリティ
  • ポータルを閉じるためのEspaceショートカット
  • 外部クリックの処理(ポータルを閉じるかどうか)
  • リンククリックの処理(ポータルを閉じるか否か)
  • ポータルツリーでReact Contextを利用可能に

react-portal または リアクトモーダル は、フルスクリーンで表示されるポップアップ、モーダル、オーバーレイに適しており、一般に画面の中央に配置されます。

リアクトテザー は、ほとんどのReact開発者に知られていませんが、最も便利なツールの一つです。 テザー は、ポータルを作成することを許可しますが、与えられたターゲットからの相対位置で、ポータルを自動的に配置します。これは、ツールチップ、ドロップダウン、ホットスポット、ヘルプボックスなどに最適です。もしあなたが、ポータルの位置決めに関して何らかの問題を抱えているのであれば absolute / relativez-index ドロップダウンがビューポートの外に出てしまうような場合、Tetherがすべてを解決してくれます。

例えば、クリックするとツールチップに展開するオンボーディングホットスポットを簡単に実装することができます。

ここからが本当のプロダクションコードです。これ以上シンプルなものはないでしょう :)

<MenuHotspots.contacts>
  <ContactButton/>
</MenuHotspots.contacts>


編集 を発見しました。 リアクトゲートウェイ これは、ポータルを好きなノードにレンダリングすることを許可するものです(必ずしもボディとは限りません)。

編集 : どうやら リアクトポッパー は、react-tetherの代替品となり得ます。 ポッパーJS は、DOMに直接触れることなく、要素の適切な位置だけを計算するライブラリで、Tetherがbodyに直接追加するのに対して、DOMノードを置きたい場所とタイミングをユーザーに選択させることができます。

編集 もあります。 リアクトスロットフィル これは興味深いもので、ツリーの好きな場所に配置した予約済み要素スロットに要素をレンダリングすることで、同様の問題を解決することができます。