How to connect state to props with mobx.js @observer when use ES6 class?

前端 未结 4 1670
逝去的感伤
逝去的感伤 2021-01-30 14:54

Let\'s take a class like this in an app with React and React Router.

@observer class Module1 extends React.Component {

  constructor (props) {
    super(props);         


        
4条回答
  •  无人及你
    2021-01-30 15:35

    One week ago we started a new project with with react and mobx, and I faced the same issue as yours. After looking around I found the best way is to use react's context. Here's how:

    The store: 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();
    

    Note: we are using singleton to make sure that it's one instance only, because the store can be used outside react components, eg. routes.js

    The routes: 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 (
      
        
        
          
        
      
    );
    

    The main component: 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;
      }
    }
    

    And finally, a component using the store: 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 (
          

    Login

    ); } }

    You can declare new stores and add them in getChildContext of App.js, and whenever you need a certain store just declare the store dependency in the component's contextTypes, and get it from this.context.

    I noticed that it's not a requirement to pass an observable as prop, just by having the @observer decorator and using any observable value in your component, mobx and mobx-react do their magic.

    By the way redux's does the same thing as explained in App.js. https://egghead.io/lessons/javascript-redux-passing-the-store-down-implicitly-via-context

    Reference:

    • http://mobxjs.github.io/mobx/refguide/observer-component.html
    • https://facebook.github.io/react/docs/context.html

提交回复
热议问题