Prenons un cours comme celui-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? En redux j'utiliserais <provider> et redux-connect mais je suis un peu perdu avec ça dans Mobx.js.

20
dagatsoin 7 mars 2016 à 21:11

4 réponses

Meilleure réponse

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 faire passer vos magasins à vos composants à l'aide du mécanisme de contexte React, similaire à Redux connect (voir cette application 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.

10
mweststrate 7 mars 2016 à 18:23

Il y a une semaine, nous avons commencé un nouveau projet avec react et mobx , et j'ai rencontré le 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:

La boutique: 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 la contextTypes composante et récupérez-la à partir de this.context.

J'ai remarqué qu'il n'est pas obligatoire de passer un observable comme accessoire, simplement en ayant le @observer décorateur et en utilisant une valeur observable dans votre composant, {{X1 }} et mobx-react font leur magie.

Au fait, <Provider store={myStore}><App /></Provider> de redux fait la même chose que celle expliquée dans App.js. https://egghead.io/lessons/javascript-redux-passing- the-store-down-implicitly via via context

Référence:

43
thaerlabs 22 mars 2016 à 20:25

Jetez un œil à tunnel de réaction. Il vous donne un composant Provider et le décorateur inject (fonctionne comme connect dans redux).

4
kuuup 26 avril 2016 à 14:18

mobx-react propose une (expérimentale - au moment de la rédaction de cet article) Provider ( composant) et inject (composant d'ordre supérieur) pour transmettre les propriétés à la hiérarchie des composants ci-dessous.

Par le haut, vous pouvez utiliser le composant Provider pour transmettre toutes les informations pertinentes. Sous le capot, le contexte 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: gestion simplifiée des états dans React et vous pouvez voir une application passe-partout minimale qui utilise l'API SoundCloud.]

15
Robin Wieruch 6 août 2016 à 10:07