2015-03-01 12 views
17

Ich habe einen öffentlichen und einen privaten Bereich in meiner App und ich möchte in der öffentlichen Ansicht überall den modalen Dialog anzeigen können. Das Modal sollte seine eigene Route haben. Ein zweiter Anwendungsfall wäre ein Profil modal im privaten Bereich.Reagiere Router nur modale Routen

Das Problem ist, dass die aktuelle Ansicht im Hintergrund verschwinden würde, wenn das Modal angezeigt wird, da das Modal kein Kind der aktuellen Ansichtsroute ist.

Da ich nicht jedem Routen den gleichen Modal hinzufügen möchte, stellt sich hier die Frage: Ist es möglich, modale Routen von den übergeordneten Routen zu entkoppeln und überall in der App ohne Hauptrendering zu zeigen? Was ist der beste Ansatz dafür? Ich habe this issue gefunden, aber scheint nicht das gleiche Problem zu sein.

Eine Aktualisierung des Browsers auf der modalen Route würde nichts im Hintergrund anzeigen, aber das ist ein Problem, mit dem ich leben könnte.

+3

Die Art und Weise beschreiben Sie es wie die Login/modal gar nicht wirklich eine Strecke klingt sehr einfach sein wird, ist, also warum Versuchst du es so zu behandeln? Es ist wirklich nur ein "Zustand" der Benutzeroberfläche, der beispielsweise bei Bedarf auf Anwendungsebene gerendert werden kann. – WiredPrairie

+0

Es ist nicht nur ein Zustand, wenn Sie ein dynamischeres Modal wie '/ user /: uid/folder /: fid' erstellen und es an mehreren Stellen verknüpfen möchten. Login ist nur ein Beispiel. – kromit

+0

Für die Nicht-Login-Modalitäten, wo würden sie ihren Status erhalten? die Kinderroute oder woanders? –

Antwort

19

Ich denke, Ihre beste Option ist wahrscheinlich, entweder die versteckten state oder Abfragezeichenfolgen (für Permalinks) oder beides, vor allem, wenn ein modales (z. B. ein Login-modal) auf jeder Seite angezeigt werden könnte. Falls Ihnen das nicht bekannt ist, stellt React Router den Teil state der Verlaufs-API zur Verfügung, mit dem Sie Daten im Verlauf des Benutzers speichern können, die in der URL nicht sichtbar sind.

Hier ist eine Reihe von Routen, die ich im Sinn habe; Sie können straight into the working example on JSBin springen, wenn Sie möchten. Sie können auch die resultierende Beispielanwendung in its own window anzeigen, sodass Sie die URLs ändern können (die Hash-Speicherort-Strategie wird aus Gründen der Kompatibilität mit JSBin verwendet) und um sicherzustellen, dass die Aktualisierung wie erwartet funktioniert.

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> 
); 

Lassen Sie uns diese Routen und ihre Komponenten untersuchen.

Zunächst ist makeComponent nur eine Methode, die eine Zeichenfolge verwendet und eine React-Komponente erstellt, die diese Zeichenfolge als Kopf und dann alle untergeordneten Elemente darstellt. Es ist nur ein schneller Weg, um Komponenten zu erstellen.

LoginRedirect ist eine Komponente mit einem Zweck: überprüfen, um zu sehen, ob der Pfad /loginoder ist, wenn es eine ist ?login Query-String auf dem aktuellen Pfad. Wenn einer der beiden Werte zutrifft, und der aktuelle Status nicht den Schlüssel login enthält, wird der Schlüssel login auf true gesetzt. Die Route wird verwendet, wenn die untergeordnete Route any übereinstimmt (dh, die Komponente wird immer gerendert).

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); 
    } 
} 

Wenn Sie keine Abfrage-Strings zur Anzeige des Login-modal verwenden möchten, können Sie natürlich auch diese Komponente ändern, um Ihre Bedürfnisse anzupassen.

Als nächstes ist LocationDisplay, aber es ist nur eine Komponente, die ich für die JSBin-Demo erstellt habe, die Informationen über den aktuellen Pfad, Status und Abfrage anzeigt und auch eine Reihe von Links anzeigt, die die Funktionalität der App demonstrieren.

Der Anmeldestatus ist wichtig für die nächste Komponente ModalCheck. Diese Komponente ist dafür zuständig, den aktuellen Status für die Schlüssel login (oder profile oder möglicherweise andere) zu überprüfen und das entsprechende Modal entsprechend anzuzeigen.(Die JSBin Demo ein super einfach modal implementiert, Sie werden sicherlich schöner. :) Es zeigt auch den Status der modalen Prüfungen in Textform auf der Hauptseite.)

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> 
    ) 
    } 
} 

Alles andere ist ziemlich Standard-Router React Sachen. Das einzige, was zu beachten ist, sind die Link s innerhalb LocationDisplay, die zeigen, wie Sie mit verschiedenen Orten in Ihrer App verknüpfen können, unter bestimmten Umständen modals.

Zunächst einmal können Sie natürlich Link (und permalink) auf jeder Seite es zu fragen mithilfe der login Schlüssel in die Query-String den Login-modal anzuzeigen:

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

Sie können natürlich auch , Link direkt zur URL /login.

Als Nächstes bemerken Sie, dass Sie den Status explizit so festlegen können, dass ein Modal angezeigt wird, und dies wird nicht die URL ändern. Es wird jedoch in der Historie beibehalten, so dass Back/Forward verwendet werden kann, wie Sie es erwarten würden, und eine Aktualisierung zeigt das Modal über die gleiche Hintergrundseite.

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

Sie können auch einen Link zur aktuellen Seite, eine bestimmte modale hinzufügen.

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> 

Natürlich, je nachdem, wie Sie Ihre Anwendung zu arbeiten, die nicht alle diese anwendbar oder nützlich ist. Zum Beispiel, wenn ein Modal ist wirklich global (das heißt, es kann angezeigt werden, egal die Route), kann dies gut funktionieren, aber für Modale wie das Profil eines bestimmten Benutzers, würde ich wahrscheinlich das machen eine separate Strecke, ist es in der übergeordneten Verschachtelung, zum Beispiel:

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

UserProfile, in diesem Fall würde eine Komponente sein, die eine modale rendert.

Ein anderes Beispiel, in dem Sie eine Änderung vornehmen möchten, ist das Speichern bestimmter Modals im Verlauf; Wenn Sie nicht möchten, verwenden Sie replaceState statt setState wie geeignet.

+0

Sehr schöne Lösung. Vielen Dank. Ich wundere mich jedoch immer noch, warum der Router so etwas eingebaut hat. – kromit

+0

Diese Antwort ist immer noch gültig (und super informativ), aber der JS Bin hat einige tote Skripte. Ich habe die History- und die read-dom-Skripte so aktualisiert, dass sie auf die neuesten Skripte auf unpkg verweisen.com https://jsbin.com/laxahozaro/edit?js,output – yourfavorite

0

Verwenden shouldComponentUpdate tun dies

<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 
 
    ) 
 
    } 
 
});

+2

bitte erklären Sie ein wenig um Ihren Code. Das würde deine Antwort verbessern. – davejal

Verwandte Themen