2016-03-07 6 views
17

Nehmen wir eine Klasse wie diese in einer App mit React und React Router.Wie verbinde ich den Status mit Requisiten mit mobx.js @observer, wenn die ES6-Klasse verwendet wird?

//index.js 
render(
     <Router history={browserHistory}> 
     <Route path="/" component={App}> 
      <Route path='/map' component={Module1} > 
      <Route path="/entity/:id" component={SubModule}/> 
      </Route> 
      <Route path='/map' component={Module2} > 
     </Route> 
     </Router>, 
     document.getElementById('render-target') 
    ); 

Wie kann ich die Requisiten module1.bars Module1 Komponente zu übergeben:

@observer class Module1 extends React.Component { 

    constructor (props) { 
    super(props); 
    //... 
    } 

    componentWillMount(){ 
    //... 
    } 

    method(){ 
    //... 
    } 

    otherMethod(){ 
    //... 
    } 

    render() { 
    return (
     <ChildComp bars={this.props.bars}/>} 
    ); 
    } 
} 

Und lassen Sie sich wie diese einen Zustand nehmen

state = observable({ 
    module1:{ 
    bars:{ 
     //... 
    } 
    }, 
    module2:{ 
    foos:{ 
     //... 
    } 
    } 
}) 

Die Module1 Komponente wird wie folgt geladen? In Redux würde ich <provider> und redux-connect verwenden, aber ich bin ein bisschen damit in Mobx.js verloren.

Antwort

8

Als erstes ist hier ein einfaches Beispiel-Anwendung, die MobX nicht Routing verwendet wird, reagieren und reagieren-Router: https://github.com/contacts-mvc/mobx-react-typescript

In der Regel persönlich Ich mag ausdrücklich alle relevanten Geschäfte als explizite Requisiten meine Komponenten zu übergeben. Sie können aber auch ein Paket wie verwenden, um Ihre Speicher mit dem React-Kontextmechanismus an Ihre Komponenten zu übergeben, ähnlich wie Redux connect (siehe dieses Beispiel app).

Sobald Sie Ihr Geschäft in Ihrer Komponente haben, analysieren Sie die Routing-Parameter in ComponentWillMount und aktualisieren Sie Ihre Geschäfte entsprechend.

Das sollte eigentlich alles sein :) Aber lassen Sie mich wissen, wenn ich etwas unbeantwortet lasse.

+0

Reagiert das Mobx-Typoskript-Beispiel, wie man sie explizit weitergibt? – dagatsoin

+0

Vielleicht habe ich eine falsche Vorstellung von der Auswirkung auf die Leistung, um einen ganzen Laden in einer oberen Komponente zu übergeben. Vielleicht ist die Antwort auf meine Frage eine andere Frage: Ist es eine gute Methode, einen ganzen Speicher an eine Komponente zu übergeben? Und die offensichtliche Antwort ist ja. Ich mache das schon mit Redux-Provider ... Bestätigst du? – dagatsoin

+2

Ja, ich bestätige. Ein Geschäft ist nur eine Referenz. Sollte sehr effizient sein, um herumzugehen :) Es stellt auch sicher, dass Sie alle "Modell" -Api zur Verfügung haben, die Sie möglicherweise in Ihrer Komponente benötigen. – mweststrate

40

Vor einer Woche haben wir begonnen, ein neues Projekt mit mit reagieren und mobx, und ich vor dem gleichen Problem wie Sie. Nachdem ich mich umgesehen habe, fand ich den besten Weg, den Kontext von von react zu verwenden. Hier ist, wie:

Shop: stores/Auth.js

import { get, post } from 'axios'; 
import { observable, computed } from 'mobx'; 
import jwt from 'jsonwebtoken'; 
import singleton from 'singleton'; 

import Storage from '../services/Storage'; 

class Auth extends singleton { 
    @observable user = null; 
    @computed get isLoggedIn() { 
    return !!this.user; 
    } 

    constructor() { 
    super(); 

    const token = Storage.get('token'); 

    if (token) { 
     this.user = jwt.verify(token, JWT_SECRET); 
    } 
    } 

    login(username, password) { 
    return post('/api/auth/login', { 
     username, password 
    }) 
    .then((res) => { 
     this.user = res.data.user; 
     Storage.set('token', res.data.token); 
     return res; 
    }); 
    } 

    logout() { 
    Storage.remove('token'); 
    return get('/api/auth/logout'); 
    } 
} 

export default Auth.get(); 

Hinweis: wir Singleton verwenden, um sicherzustellen, dass es eine Instanz nur, weil der Speicher verwendet werden kann, außerhalb reagieren Komponenten, zB. routes.js

Die Routen: routes.js

import React from 'react'; 
import { Route, IndexRoute } from 'react-router'; 

import App from './App'; 
import Login from './Login/Login'; 
import Admin from './Admin/Admin'; 
import Dashboard from './Admin/views/Dashboard'; 
import Auth from './stores/Auth'; // note: we can use the same store here.. 

function authRequired(nextState, replace) { 
    if (!Auth.isLoggedIn) { 
    replace('/login'); 
    } 
} 

export default (
    <Route name="root" path="/" component={App}> 
    <Route name="login" path="login" component={Login} /> 
    <Route name="admin" path="admin" onEnter={authRequired} component={Admin}> 
     <IndexRoute name="dashboard" component={Dashboard} /> 
    </Route> 
    </Route> 
); 

Die Hauptkomponente: App.js

// App.js 
import React, { Component } from 'react'; 
import Auth from './stores/Auth'; 

export default class App extends Component { 

    static contextTypes = { 
    router: React.PropTypes.object.isRequired 
    }; 

    static childContextTypes = { 
    store: React.PropTypes.object 
    }; 

    getChildContext() { 
    /** 
    * Register stores to be passed down to components 
    */ 
    return { 
     store: { 
     auth: Auth 
     } 
    }; 
    } 

    componentWillMount() { 
    if (!Auth.isLoggedIn) { 
     this.context.router.push('/login'); 
    } 
    } 

    render() { 
    return this.props.children; 
    } 
} 

Und schließlich eine Komponente den Laden mit: Login.js

import React, { Component } from 'react'; 
import { observer } from 'mobx-react'; 

@observer 
export default class Login extends Component { 

    static contextTypes = { 
    router: React.PropTypes.object.isRequired, 
    store: React.PropTypes.object.isRequired 
    }; 

    onSubmit(e) { 
    const { auth } = this.context.store; // this is our 'Auth' store, same observable instance used by the `routes.js` 

    auth.login(this.refs.username.value, this.refs.password.value) 
     .then(() => { 
     if (auth.isLoggedIn) this.context.router.push('/admin'); 
     }) 
     .catch((err) => { 
     console.log(err); 
     }); 

    e.preventDefault(); 
    } 

    render() { 
    return (
     <div className="login__form"> 
     <h2>Login</h2> 
     <form onSubmit={this.onSubmit.bind(this)}> 
      <input type="text" ref="username" name="username" placeholder="Username" /> 
      <input type="password" ref="password" name="password" placeholder="Password" /> 
      <button type="submit">Login</button> 
     </form> 
     </div> 
    ); 
    } 
} 

Sie können neue Geschäfte erklären und fügen Sie sie inhinzuvon App.js, und wann immer Sie einen bestimmten Speicher benötigen, deklarieren Sie einfach die store Abhängigkeit in contextTypes der Komponente, und erhalten Sie es von this.context.

Ich bemerkte, dass es nicht erforderlich ist, ein beobachtbaren als Stütze passiert, nur durch die @observer Dekorateur hat und jeden beobachtbaren Wert in Ihrer Komponente verwenden, mobx und mobx-react ihre Magie tun.

Übrigens tut Redux <Provider store={myStore}><App /></Provider> das Gleiche wie in App.js erläutert. https://egghead.io/lessons/javascript-redux-passing-the-store-down-implicitly-via-context

Referenz:

+0

Danke für das Teilen! – mweststrate

+0

wirklich tolle Lösung, vielen Dank! – Alex

+0

Tolle Lösung, danke! Frage: Warum kommst du zurück? This.user? Ist das etwas anderes als "this.user" zurückgeben? –

4

einen Blick auf react-tunnel nehmen. Es gibt Ihnen eine Provider Komponente und die inject Dekorateur (funktioniert wie connect in redux).

15

mobx-react bietet eine (experimentelle - zum Zeitpunkt des Schreibens dieser) Provider (Komponente) und inject (Komponente höherer Ordnung), um Eigenschaften an die Komponentenhierarchie weiterzugeben.

Von oben können Sie die Provider Komponente verwenden, um alle relevanten Informationen zu übergeben. Unter der Haube wird React context verwendet.

import { Provider } from 'mobx-react'; 
... 
import oneStore from './stores/oneStore'; 
import anotherStore from './stores/anotherStore'; 

const stores = { oneStore, anotherStore }; 

ReactDOM.render(
    <Provider { ...stores }> 
    <Router history={browserHistory}> 
     <Route path="/" component={App}> 
     <Route path="/" component={SomeComponent} /> 
     </Route> 
    </Router> 
    </Provider>, 
    document.getElementById('app') 
); 

In SomeComponent können Sie die übergebenen Objekte abrufen, indem Sie die inject HOC mit:

import { observer, inject } from 'mobx-react'; 
... 

const SomeComponent = inject('oneStore', 'anotherStore')(observer(({ oneStore, anotherStore }) => { 
    return <div>{oneStore.someProp}{anotherStore.someOtherProp}</div>; 
})) 

export default SomeComponent; 

[Disclaimer: Ich schrieb darüber in MobX React: Simplified State Management in React und Sie können eine minimal boilerplate application sehen, die die Soundcloud API verbraucht.]