使用ES6类时如何使用mobx.js @observer将状态连接到props?

时间:2016-03-07 18:11:32

标签: javascript reactjs mobx

让我们在React和React Router的应用程序中选择这样的类。

@observer class Module1 extends React.Component {

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

  componentWillMount(){
    //...
  }

  method(){
    //...
  }

  otherMethod(){
    //...
  }

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

让我们采取这样的状态

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

Module1组件加载如下:

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

我怎样才能将道具module1.bars传递给Module1组件? 在redux中,我会使用<provider>redux-connect,但我在Mobx.js中有点迷失。

4 个答案:

答案 0 :(得分:42)

一周前,我们开始了一个新项目,其中包含反应 mobx ,我遇到了与您相同的问题。环顾四周后,我发现最好的方法是使用react的上下文。方法如下:

商店:private async Task<bool> delay() { await Task.Delay(1000); return true; }

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

路线:routes.js

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

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

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> ); } } getChildContext中,并且只要您需要某个商店,只需在组件App.js中声明store相关性,从contextTypes获得。

我注意到,只要拥有this.context装饰器并在组件中使用任何可观察值,就不需要将可观察作为道具传递, @observermobx发挥他们的魔力。

顺便说一句,redux的mobx-react<Provider store={myStore}><App /></Provider>中解释的相同。 https://egghead.io/lessons/javascript-redux-passing-the-store-down-implicitly-via-context

参考:

答案 1 :(得分:15)

mobx-react提供了一个(实验 - 在撰写本文时)Provider(组件)和inject(高阶组件)将属性传递给下面的组件层次结构。

从上面您可以使用Provider组件传递所有相关信息。在引擎盖下使用React上下文。

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

SomeComponent中,您可以使用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;

[免责声明:我在MobX React: Simplified State Management in React中写过这篇文章,您可以看到使用SoundCloud API的minimal boilerplate application。]

答案 2 :(得分:10)

首先,这是一个使用MobX,React和react-router进行路由的简单示例应用程序:https://github.com/contacts-mvc/mobx-react-typescript

一般来说,我个人喜欢明确地将所有相关商店作为显式道具传递给我的组件。但您也可以使用像Ryan这样的包来使用React上下文机制将您的商店传递给您的组件,类似于Redux connect(请参阅此app示例)。

在您的组件中存放商店后,解析ComponentWillMount中的路由参数并相应地更新商店。

那应该基本上都是:)但是如果我没有回答任何问题,请告诉我。

答案 3 :(得分:4)

看看react-tunnel。它为您提供了Provider组件和inject装饰器(在redux中的工作方式类似connect)。