2014-10-13 10 views
19

Come posso aggiornare un componente ReactJS in base a URL/percorso quando si utilizza React-Router?Come aggiornare il componente ReactJS in base a URL/percorso con React-Router

Il codice seguente funziona, ma è questo il modo corretto per farlo? Sembra un sacco di codice per fare un semplice aggiornamento. Speravo che ci sarebbe stata una chiamata API stateful nel router per occuparsi automaticamente di questo scenario.

var MyHomeView = React.createClass({ 
    componentDidMount: function() { 
     this.props.updateHeader(); 
    }, 

    render: function() { 
     return (
     <div> 
      <h2>Home</h2> 
     </div> 
    ); 
    } 
}); 


var MyAboutView = React.createClass({ 
    componentDidMount: function() { 
     this.props.updateHeader(); 
    }, 

    render: function() { 
    return (
     <div className="my-page-text"> 
     <h2>About</h2> 
     </div> 
    ); 
    } 
}); 


var MyHeader = React.createClass({ 
    mixins: [ CurrentPath ], 

    getInitialState: function() { 
    return { 
     myPath: "about", 
     classes: "ion-ios7-information" 
    }; 
    }, 

    updateHeader: function() {  
     // Classnames refer to www.ionicons.com 
    if (this.getCurrentPath() === "/") { 
     this.setState({myPath: "about" }); 
     this.setState({classes: "ion-ios7-information" }); 
    } else { 
     this.setState({myPath: "/" }); 
     this.setState({classes: "ion-ios7-rewind" }); 
    }  
    }, 

    render: function() { 
    return (
     <Link to={this.state.myPath}> 
      <i className={this.state.classes} /> 
     </Link> 
    ); 
    } 
}); 


var App = React.createClass({ 
    updateHeader: function() { 
     this.refs.header.updateHeader(); 
    }, 

    render: function() { 
     return (
     <div> 
      <MyHeader ref="header" /> 

     <this.props.activeRouteHandler updateHeader={this.updateHeader} /> 
     </div> 
    ); 
    } 
}); 


React.renderComponent((
    <Routes> 
    <Route path="/" handler={App}> 
     <DefaultRoute handler={MyHomeView} /> 
     <Route name="about" handler={MyAboutView} /> 
    </Route> 
    </Routes> 
), document.body); 
+1

Perché mantieni "myPath" e "classe" è in stato? Dato che hai il "getCurrentPath()", potresti semplicemente calcolare i valori di link e className all'interno della funzione di rendering? O è "getCurrentPath" non aggiornato correttamente nel tuo caso? – phtrivier

+0

Dovrò verificarlo, se 'getCurrentPath()' è in stato, allora dovrebbe funzionare senza che io tenga lo stato extra in 'myPath'. Dovrei provare questo come per esempio nei commenti di questo file: https://github.com/rackt/react-router/blob/master/modules/mixins/CurrentPath.js –

risposta

3

Questa domanda è stata aperta un po ', e sembra che ci dovrebbe essere una soluzione più semplice, ma mi prendo una pugnalata e condividere ciò che facciamo nella nostra applicazione.

Stiamo utilizzando l'architettura Flux, che ha la nozione di Negozi che informano i componenti quando il loro stato è aggiornato. In react-router, hanno uno PathStore che si adatta bene a questo modello, poiché quando l'URL lo modifica può quindi notificare componenti che hanno cura e devono essere aggiornati.

Il StoreListener che utilizziamo nella nostra applicazione è disponibile al pubblico qui: https://github.com/odysseyscience/react-flux-suppprt. Avrei dovuto pubblicare su NPM, ma non ancora. Lo farò presto se pensi che sarebbe utile.

Ecco come usiamo il nostro StoreListener per ascoltare le modifiche sul PathStore da react-router, e come lo si dovrebbe utilizzare nel vostro MyHeader:

var StoreListener = require('path/to/StoreListener'); 
var PathStore = require ('react-router/modules/stores/PathStore'); 

var MyHeader = React.createClass({ 
    mixins: [ 
    StoreListener(PathStore) 
    ], 

    getStateFromStores: function() { 
    return { 
     path: PathStore.getCurrentPath() 
    }; 
    }, 

    render: function() { 
    var path = this.state.path; 
    var myPath; 
    var classes; 

    if (this.state.path === "/") { 
     myPath = 'about'; 
     classes = 'ion-ios7-information'; 
    } else { 
     myPath = '/'; 
     classes = 'ion-ios7-rewind'; 
    } 

    return (
     <Link to={myPath}> 
     <i className={classes} /> 
     </Link> 
    ); 
    } 
}); 

Questo inizia con una mixin che dice "mi preoccupo per i cambiamenti al PathStore ". Ogni volta che questo negozio (o qualsiasi negozio viene ascoltato) cambia, getStateFromStores() viene chiamato sul componente per recuperare i dati dal negozio che si desidera disponibile su this.state. Se/Quando i dati cambiano, viene chiamato di nuovo render() e si rilegge this.state e si applicano le modifiche.

Questo è esattamente il nostro modello per l'applicazione di determinate classi CSS "attive" nelle schede di intestazione o in qualsiasi altro punto dell'applicazione che dipende dall'URL corrente.

Si noti che usiamo webpack per raggruppare i nostri moduli CommonJS, quindi potrebbero esserci alcune ipotesi sull'ambiente che può/potrebbe non funzionare per voi.

Non ho risposto a questa domanda un mese fa quando l'ho visto per la prima volta perché ritenevo che ci fosse un modo migliore, ma forse la nostra soluzione può aiutare gli altri con lo stesso problema.

+0

Bella risposta, dovrò provare questo fuori, come non ho mai usato Flux. Stavo anche giocando con lo stato del percorso in ma non vedo alcun modo per setState() nel componente senza passare oggetti di scena a bambini come me. –

+0

Puoi riassumere ciò che aggiungi a Flux in questo repository e perché il Flusso di base non è sufficiente: https://github.com/odysseyscience/react-flux-suppprt –

+0

Per "flusso di base", intendi [questo repo] (https://github.com/facebook/flux)? Se è così, questo è esattamente il codice che stavo aumentando con il codice nel mio repository. Fondamentalmente, 'StoreListener' ti dà un metodo conveniente (' getStateFromStores') per accedere ai dati dai tuoi negozi e impostarli su 'this.state'. Si lega e si separa tramite 'componentDidMount' e' componentWillUnmount', e chiama 'getStateFromStores' ogni volta che qualcosa cambia. Quindi hai molto meno testo da scrivere. 'BaseStore' è semplicemente tutto il testo che si vorrebbe scrivere in ciascuno dei tuoi negozi. Basta estenderlo per comodità. –

5

Questo è stato aggiornato se si sta lavorando con il react-router> v11.0.

Si può leggere the details here

TLDR:

// v0.11.x 
var Something = React.createClass({ 
    mixins: [ Router.State ], 
    render: function() { 
     var path = this.getPath(); 
    } 
}); 

Per l'intero State API: https://github.com/rackt/react-router/blob/master/doc/04%20Mixins/State.md

+0

Questo è stato modificato in react-router v1.0. Vedi: https://github.com/rackt/react-router/blob/master/CHANGES.md#state-mixin – Neta

+0

Link "i dettagli qui" sono morti ... Non dovresti mai collegare a "master" come cambia tutto il tempo. Scegli invece il 'tag' che corrisponde alla versione corrente e usa quel link. –

7

In reagire-router 2.0.0 è possibile utilizzare il hashHistory o browserHistory:

browserHistory.listen(function(ev) { 
    console.log('listen', ev.pathname); 
}); 

<Router history={browserHistory}>{routes}</Router> 
+1

Ho cercato questa risposta per tutta la mattinata. Ora, dove sarebbe il posto migliore per metterlo? componente principale dell'app? –

+0

Sono contento che questo codice ti aiuti! Ma non capisco la tua domanda. – Dherik

+1

@ yarden.refaeli se usi Flux, penso che dovresti aggiungere un singolo listener alla tua app principale, che aggiorna lo stato di un negozio. Quindi ciascun componente può decidere di ascoltare quel negozio e definire i propri gestori per le modifiche. –