react decorators javascript reactjs mobx

javascript - decorators - ¿Cómo conectar el estado a los accesorios con mobx.js @observer cuando se usa la clase ES6?



mobx-react (4)

Tomemos una clase como esta en una aplicación con React y React Router.

@observer class Module1 extends React.Component { constructor (props) { super(props); //... } componentWillMount(){ //... } method(){ //... } otherMethod(){ //... } render() { return ( <ChildComp bars={this.props.bars}/>} ); } }

Y tomemos un estado como este

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

El componente Module1 se carga así:

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

¿Cómo podría pasar el props module1.bars al componente Module1? En redux usaría <provider> y redux-connect pero estoy un poco perdido con esto en Mobx.js.


Echa un vistazo a react-tunnel . Le proporciona un componente de Provider y el decorador de inject (funciona como connect en redux).


Hace una semana comenzamos un nuevo proyecto con reac y mobx , y enfrenté el mismo problema que el tuyo. Después de mirar alrededor, encontré que la mejor manera es usar el contexto de reaccion. Así es cómo:

La tienda: 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();

Nota: estamos usando Singleton para asegurarnos de que sea solo una instancia, ya que la tienda se puede usar fuera de los componentes de reacción, por ejemplo. routes.js

Las rutas: 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> );

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

Y finalmente, un componente que usa la tienda: 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> ); } }

Puede declarar nuevas tiendas y agregarlas en getChildContext de App.js , y cada vez que necesite una tienda determinada, simplemente declare la dependencia de la store en los contextTypes de contextTypes del componente, y this.context de this.context .

Noté que no es un requisito pasar un observable como prop, solo con tener el decorador @observer y usar cualquier valor observable en su componente, mobx y mobx-react hacen su magia.

Por cierto, <Provider store={myStore}><App /></Provider> redux hace lo mismo que se explica en App.js https://egghead.io/lessons/javascript-redux-passing-the-store-down-implicitly-via-context

Referencia:


Primero, aquí hay una aplicación de ejemplo simple que realiza enrutamiento utilizando MobX, React y react-router: https://github.com/contacts-mvc/mobx-react-typescript

En general, personalmente me gusta pasar explícitamente todas las tiendas relevantes como accesorios explícitos a mis componentes. Pero también puede usar un paquete como Ryan para que sus tiendas pasen a sus componentes utilizando el mecanismo de contexto React, similar a Redux connect (consulte esta app para ver un ejemplo).

Una vez que tenga su tienda en su componente, analice los parámetros de enrutamiento en ComponentWillMount y actualice sus tiendas en consecuencia.

Básicamente, eso debería ser todo :) Pero avísame si dejo algo sin respuesta.


mobx-react ofrece un Provider (componente) experimental (al momento de escribir esto) e inject (componente de orden superior) para pasar las propiedades a la jerarquía de componentes a continuación.

Desde arriba puede usar el componente Provider para pasar toda la información relevante. Bajo el capó se utiliza el contexto 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'') );

En SomeComponent puede recuperar las propiedades pasadas usando el HOC de inject :

import { observer, inject } from ''mobx-react''; ... const SomeComponent = inject(''oneStore'', ''anotherStore'')(observer(({ oneStore, anotherStore }) => { return <div>{oneStore.someProp}{anotherStore.someOtherProp}</div>; })) export default SomeComponent;

[Descargo de responsabilidad: escribí sobre esto en MobX React: Simplified State Management en React y puede ver una aplicación de boilerplate mínima que consume la API de SoundCloud.]