24

アプリにパブリック エリアとプライベート エリアがあり、パブリック ビューのどこにでもログイン モーダル ダイアログを表示できるようにしたいと考えています。モーダルには独自のルートが必要です。2 番目のユース ケースは、プライベート エリアのプロファイル モーダルです。

問題は、モーダルが現在のビュールートの子ではないため、モーダルが表示されるとバックグラウンドの現在のビューが消えることです。

すべての可能なビュー ルートに同じモーダルを追加したくないので、ここで質問があります: 親ルートからモーダル ルートを分離し、メイン コンテンツのレンダリングなしでアプリのどこにでも表示することは可能ですか? これに最適なアプローチは何ですか?この問題を見つけましたが、同じ問題ではないようです。

モーダル ルートでブラウザを更新すると、バックグラウンドで何もレンダリングされませんが、それは私が対処できる問題です。

4

2 に答える 2

24

state特にモーダル(ログインモーダルなど)を任意のページに表示できる場合は、隠し文字列またはクエリ文字列(パーマリンクの場合)、またはその両方を利用するのが最善の方法だと思います。ご存じないかもしれませんが、React Routerstateは履歴 API の一部を公開しているため、実際には URL には表示されないデータをユーザーの履歴に保存できます。

これが私が考えている一連のルートです。必要に応じて、JSBinの実際の例に直接ジャンプできます。結果のサンプル アプリを独自のウィンドウで表示して、URL の変更 (JSBin との互換性のためにハッシュ ロケーション戦略を使用) を確認し、期待どおりに更新が機能することを確認することもできます。

const router = (
  <Router>
    <Route component={LoginRedirect}>
      <Route component={LocationDisplay}>
        <Route path="/" component={ModalCheck}>
          <Route path="/login" component={makeComponent("login")} />
          <Route path="/one" component={makeComponent("one")} />
          <Route path="/two" component={makeComponent("two")} />
          <Route path="/users" component={makeComponent("users")}>
            <Route path=":userId" component={UserProfileComponent} />
          </Route>
        </Route>
      </Route>
    </Route>
  </Router>
);

これらのルートとそのコンポーネントを調べてみましょう。

まず第一にmakeComponent、文字列を受け取り、その文字列をヘッダーとしてレンダリングし、次にすべての子としてレンダリングする React コンポーネントを作成する単なるメソッドです。コンポーネントを作成する簡単な方法です。

LoginRedirect/login パスが存在するかどうか、または?login現在のパスにクエリ文字列が存在するかどうかを確認するという 1 つの目的を持つコンポーネントです。これらのいずれかが true で現在の状態にキーが含まれていない場合、状態のキーloginを に設定します。いずれかの子ルートが一致する場合、そのルートが使用されます (つまり、コンポーネントは常にレンダリングされます)。logintrue

class LoginRedirect extends React.Component {
  componentWillMount() {
    this.handleLoginRedirect(this.props);
  }

  componentWillReceiveProps(nextProps) {
    this.handleLoginRedirect(nextProps);
  }

  handleLoginRedirect(props) {
    const { location } = props;
    const state = location.state || {};
    const onLoginRoute = location.query.login || location.pathname === "/login";
    const needsStateChange = onLoginRoute && !state.login;
    if (needsStateChange) {
      // we hit a URL with ?login in it
      // replace state with the same URL but login modal state
      props.history.setState({login: true});
    }
  }

  render() {
    return React.Children.only(this.props.children);
  }
}

ログインモーダルを表示するためにクエリ文字列を使用したくない場合は、もちろんこのコンポーネントをニーズに合わせて変更できます。

次はLocationDisplay.

ログイン状態は、次のコンポーネント にとって重要ですModalCheckloginこのコンポーネントは、 (またはprofile、場合によってはその他の) キーの現在の状態をチェックし、関連するモーダルを適切に表示する役割を果たします。(JSBin のデモでは、非常にシンプルなモーダルが実装されています。あなたのモーダルの方が確実に優れています。:) また、モーダル チェックのステータスがメイン ページのテキスト形式で表示されます。)

class ModalCheck extends React.Component {
  render() {
    const location = this.props.location;
    const state = location.state || {};
    const showingLoginModal = state.login === true;
    const showingProfileMoal = state.profile === true;

    const loginModal = showingLoginModal && <Modal location={location} stateKey="login"><LoginModal /></Modal>;
    const profileModal = showingProfileMoal && <Modal location={location} stateKey="profile"><ProfileModal /></Modal>;

    return (
      <div style={containerStyle}>
        <strong>Modal state:</strong>
        <ul>
          <li>Login modal: {showingLoginModal ? "Yes" : "No"}</li>
          <li>Profile modal: {showingProfileMoal ? "Yes" : "No"}</li>
        </ul>
        {loginModal}
        {profileModal}
        {this.props.children}
      </div>
    )
  }
}

他のすべては、かなり標準的な React Router のものです。注意すべき唯一のことは、特定の状況でモーダルを表示して、アプリ内のさまざまな場所にリンクする方法を示すLink内部のです。LocationDisplay

まず第一にlogin、クエリ文字列のキーを使用して、ログインモーダルを表示するように要求する任意のページにリンク (およびパーマリンク) することができます。

<Link to="/one" query={{login: 1}}>/one?login</Link>
<Link to="/two" query={{login: 1}}>/two?login</Link>

もちろん、/loginURLに直接リンクすることもできます。

次に、モーダルが表示されるように状態を明示的に設定できることに注意してください。これにより、URL は変更されません。ただし、履歴には保持されるため、期待どおりに戻る/進むことができ、更新すると同じ背景ページの上にモーダルが表示されます。

<Link to="/one" state={{login: true}}>/one with login state</Link>
<Link to="/two" state={{login: true}}>/two with login state</Link>

特定のモーダルを追加して、現在のページにリンクすることもできます。

const path = props.location.pathname;

<Link to={path} state={{login: true}}>current path + login state</Link>
<Link to={path} state={{profile: true}}>current path + profile state</Link>

もちろん、アプリをどのように動作させたいかによっては、これらすべてが適用可能または有用であるとは限りません。たとえば、モーダルが真にグローバルでない限り (つまり、 route に関係なく表示できる場合)、これはうまくいくかもしれませんが、特定のユーザーのプロファイルを表示するようなモーダルの場合は、おそらくそれを別のルートにするでしょう。親にネストします。例:

<Route path="/users/:id" component={UserPage}>
  <Route path="/users/:id/profile" component={UserProfile} />
</Route>

UserProfileこの場合、モーダルをレンダリングするコンポーネントになります。

変更を加えたい別の例は、特定のモーダルを履歴に保存することです。したくない場合は、必要に応じてreplaceState代わりに使用してくださいsetState

于 2015-10-04T05:06:53.560 に答える
0

これを行うには shouldComponentUpdate を使用します。これは非常に簡単です

<Router history={newHistory}>
        <Route path="/" component={App}>
          <Route path="home" component={Container} >
            <IndexRedirect to="home"/>
            <Route path="login" component={Authentication}/>
            <Route path="home" component={Home}/>
            <Route path="post/:id" component={Post}/>
          </Route>
        </Route>
</Router>


<Link to='/post/123' state={{isModal:true}}/>
<Link to='/home' state={{isModal:false}}/>
<Link to='/login' state={{isModal:true}}/>


Container = React.createClass({
  render() {
    let isModal = (location.state && location.state.modal);
      return (
        <div id="MeContainer">
          <ModalControlOpen isModal={isModal}>
            <Modal returnTo={this.props.location.pathname}>
              {this.props.children}
            </Modal>
          </ModalControlOpen>
          <ModalControlClose isModal={isModal}>
            {this.props.children}
          </ModalControlClose>
        </div>
      )
   
  }
});

ModalControlOpen = React.createClass({
  render(){
    if (this.props.isModal) {
      return (

        this.props.children
      )
    } else return <div></div>

  }
});

ModalControlClose = React.createClass({
  shouldComponentUpdate(nextProp){
    return !nextProp.isModal
  },
  render(){
    return (
      this.props.children
    )
  }
});

于 2016-01-16T03:17:27.043 に答える