Prenons une classe comme celle-ci dans une application avec React et React Router.
@observer class Module1 extends React.Component {
constructor (props) {
super(props);
//...
}
componentWillMount(){
//...
}
method(){
//...
}
otherMethod(){
//...
}
render() {
return (
<ChildComp bars={this.props.bars}/>}
);
}
}
Et prenons un état comme celui-ci
state = observable({
module1:{
bars:{
//...
}
},
module2:{
foos:{
//...
}
}
})
Le composant Module1 est chargé comme ceci:
//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')
);
Comment pourrais-je passer les accessoires module1.bars
au composant Module1? Dans redux, j'utiliserais <provider>
et redux-connect
mais je suis un peu perdu avec ça dans Mobx.js.
Tout d'abord, voici un exemple d'application simple qui fait du routage à l'aide de MobX, React et react-router: https://github.com/contacts-mvc/mobx-react-TypeScript
En général, personnellement, j'aime transmettre explicitement tous les magasins concernés en tant qu'accessoires explicites à mes composants. Mais vous pouvez également utiliser un package comme Ryan pour que vos magasins soient transmis à vos composants en utilisant le mécanisme de contexte React, similaire à Redux connect (voir ceci app pour un exemple).
Une fois que vous avez votre magasin dans votre composant, analysez les paramètres de routage dans ComponentWillMount et mettez à jour vos magasins en conséquence.
Cela devrait être tout simplement :) Mais faites-moi savoir si je laisse quelque chose sans réponse.
Il y a une semaine, nous avons commencé un nouveau projet avec avec react et mobx , et j'ai fait face au même problème que le vôtre. Après avoir regardé autour de moi, j'ai trouvé que le meilleur moyen était d'utiliser le contexte de react . Voici comment:
Le magasin: 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();
Remarque: nous utilisons singleton pour nous assurer qu'il ne s'agit que d'une seule instance, car le magasin peut être utilisé en dehors des composants React, par exemple. routes.js
Les itinéraires: 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>
);
Le composant principal: 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;
}
}
Et enfin, un composant utilisant le magasin: 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>
);
}
}
Vous pouvez déclarer de nouveaux magasins et les ajouter dans getChildContext
de App.js
, et chaque fois que vous avez besoin d'un certain magasin, déclarez simplement la dépendance store
dans le contextTypes
du composant, et récupérez-la à partir de this.context
.
J'ai remarqué qu'il n'est pas obligatoire de passer un observable comme accessoire, juste en ayant le @observer
décorateur et en utilisant n'importe quelle valeur observable dans votre composant, mobx
et mobx-react
faire leur magie.
Soit dit en passant, <Provider store={myStore}><App /></Provider>
fait la même chose que celle expliquée dans App.js
. https://egghead.io/lessons/javascript-redux-passing-the-store-down-implicitly-via-context
Référence:
mobx-react offre un (expérimental - au moment d'écrire ceci) Provider
(composant) et inject
(composant d'ordre supérieur) pour passer des propriétés à la hiérarchie des composants au dessous de.
Par le haut, vous pouvez utiliser le composant Provider
pour transmettre toutes les informations pertinentes. Sous le capot React est utilisé.
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')
);
Dans SomeComponent
, vous pouvez récupérer les propriétés transmises en utilisant le inject
HOC:
import { observer, inject } from 'mobx-react';
...
const SomeComponent = inject('oneStore', 'anotherStore')(observer(({ oneStore, anotherStore }) => {
return <div>{oneStore.someProp}{anotherStore.someOtherProp}</div>;
}))
export default SomeComponent;
[Avertissement: J'ai écrit à ce sujet dans MobX React: Simplified State Management dans React et vous pouvez voir un application minimale standard qui consomme l'API SoundCloud.]
Jetez un œil à react-tunnel . Il vous donne un composant Provider
et le décorateur inject
(fonctionne comme connect
dans redux).