Javascript 使用ES6类时,如何使用mobx.js@observer将状态连接到道具?

Javascript 使用ES6类时,如何使用mobx.js@observer将状态连接到道具?,javascript,reactjs,mobx,Javascript,Reactjs,Mobx,让我们在一个带有React和React路由器的应用程序中学习这样的课程 @observer class Module1 extends React.Component { constructor (props) { super(props); //... } componentWillMount(){ //... } method(){ //... } otherMethod(){ //... } render

让我们在一个带有React和React路由器的应用程序中学习这样的课程

@observer class Module1 extends React.Component {

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

  componentWillMount(){
    //...
  }

  method(){
    //...
  }

  otherMethod(){
    //...
  }

  render() {
    return (
       <ChildComp bars={this.props.bars}/>}
    );
  }
}
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')
    );
//index.js
渲染(
,
document.getElementById('render-target')
);
如何将道具
module1.bar
传递给module1组件?
在redux中,我会使用
redux connect
,但我对Mobx.js中的这一点有点迷茫。

首先,下面是一个使用Mobx、React和React路由器进行路由的简单示例应用程序:

一般来说,我个人喜欢将所有相关存储作为显式道具显式地传递给我的组件。但是,您也可以使用一个包,比如使用React上下文机制将存储传递给组件,类似于Redux connect(参见此示例)

一旦组件中有了存储,解析组件中的路由参数将相应地装载和更新存储


基本上就是这样:)但是如果我有什么问题没有回答,请告诉我。

一周前,我们开始了一个新项目,使用reactmobx,我面临着与您相同的问题。环顾四周后,我发现最好的方法是使用react的上下文。以下是方法:

商店:
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();
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
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;
  }
}
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>
    );
  }
}
注意:我们使用singleton来确保它只是一个实例,因为存储可以在react组件之外使用,例如
routes.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();
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
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;
  }
}
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>
    );
  }
}
路由:
routes.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();
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
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;
  }
}
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>
    );
  }
}
最后是一个使用store的组件:
Login.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();
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
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;
  }
}
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>
    );
  }
}
import React,{Component}来自'React';
从'mobx react'导入{observer};
@观察者
导出默认类登录扩展组件{
静态上下文类型={
路由器:React.PropTypes.object.isRequired,
存储:React.PropTypes.object.isRequired
};
提交(e){
const{auth}=this.context.store;//这是我们的“auth”存储,与“routes.js”使用的可观察实例相同`
身份验证登录(this.refs.username.value,this.refs.password.value)
.然后(()=>{
if(auth.isLoggedIn)this.context.router.push('/admin');
})
.catch((错误)=>{
控制台日志(err);
});
e、 预防默认值();
}
render(){
返回(
登录
登录
);
}
}
您可以在
App.js
getChildContext
中声明新的存储并添加它们,并且每当您需要某个存储时,只需在组件的
contextTypes
中声明
存储
依赖项,并从
此.context
获取它

我注意到,只需使用
@observer
装饰器并在组件中使用任何可观察的值,
mobx
mobx react
就可以发挥它们的魔力,而不需要将可观察的作为道具传递

顺便说一下,redux的

参考:

看一看。它为您提供了一个
提供者
组件和
注入
装饰器(类似于redux中的
连接

提供了一个(实验性的-在撰写本文时)
提供者
(组件)和
注入
(高阶组件)来将属性传递给下面的组件层次结构

从上面可以使用
提供程序
组件传递所有相关信息。在引擎盖下使用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')
);

[免责声明:我在中写到了这一点,您可以看到一个使用SoundCloud API的应用程序。]

mobx react typescript示例是否说明了如何显式传递它们?也许我对在顶级组件中传递整个存储的性能影响有错误的想法。也许我的问题的答案是另一个问题:将整个存储区传递给组件是否是一种良好的做法。显而易见的答案是肯定的。我已经和redux提供商做过了。。。你确认吗?是的,我确认。商店只是一个参考。传递应该非常有效:)它还可以确保组件中可能需要的所有“模型”api都可用。Thx解释:)直接在组件中导入存储而不是通过路由器层次结构传递存储是错误的吗?非常好的解决方案,谢谢!问:你为什么
返回!!这个用户
?这与只返回this.user有什么不同吗?@BarryMichaelDoyle这只是一个简写:
如果(this.user)返回true;返回false哦哦,那很方便。谢谢如果执行
const authStore=new Auth();导出默认authStore
然后
从“/authStore”导入authStore
即使您从不同的模块多次导入模块,您也应该已经有了一个单例。不是吗?你能澄清一下这方面吗?您的
singleton
模块是npm还是标准节点模块?我不知道this@Leonardo没错,
singleton
只是我一直在使用的一个实用程序,直到我发现CommonJs模块被缓存,这意味着它不会反复运行相同的
new Auth()
。很好的洞察力,谢谢!