Giter VIP home page Giter VIP logo

mobx-reactor's Introduction


EXPERIMENTAL โ€” WORK-IN-PROGRESS

A pragmatic library for simply connecting MobX data stores and asynchronous actions to functional stateless React components with a flux-like architecture inspired by redux, react-redux and redux-saga.

screenshot

Benefits

The intent of the library is to provide straightforward flux-style state management with a pragmatic trade-off between formality and convenience. Keep in mind as you evaluate this approach that your application may be better suited to different trade-offs (i.e. a more formal but less "convenient" alternative).

Simple Organization

Conveniently organize state, initializers and action handlers that modify that state together as "models" while retaining the flexibility to keep them separate when that works better.

Functional Views

Simply connect your state and actions to pure React components (i.e. props-only) using React context and a straightforward connect wrapper for one-way data flow.

Easy Async

Write declarative asynchronous logic naturally using generator functions for managing side effects without forfeiting control.

Built-in Efficiency

Built on MobX to efficiently manage the impact of data changes on recalculations and re-renders. MobX makes it easy to write memoizing derived views of your data and structure your application for fine-grained reactive updates without jumping through hoops.

Decoupled Testing

Test your functional views independent of your state, test your state independent of your views, and test your actions handlers independent of async dependencies like backend servers elementally without writing extensive mocks.

Building Blocks

The mobx-reactor building blocks are a Store, composed of Substore objects, together with Action methods connected to React components through a StoreContext and a connect wrapper.

Store

A single shared Store holds application state and provides a method to dispatch actions to affect application logic. The store is assembled from Substore sub-objects that each define and manage a piece of the application state. A Store can be configured with middleware that provides an extension point for performing activities like logging and crash reporting.

const store = new Store(
  {
    todoList: new TodoList(),
  },
  [
    logger()
  ]
)

The logger currently requires you to first npm install deep-diff.

Substores

A Substore provides a construct to organize state in sub-stores with methods that operate on that state in response to relevant actions. A sub-store objects are a vehicle for organization, not encapsulation. While all state is defined through this stores, actions can be defined as part of a store or independently of a store.

class TodoList extends Substore {
  @observable todos = map({});

  @computed get unfinishedTodoCount() {
    return this.todos.values().filter(todo => !todo.isFinished).length;
  }

  @action('addTodo')
  addTodo(title) {
    const todo = new Todo(title)
    this.todos.set(todo.id, todo)
    return dispatch('saveTodo')(todo.id)
  }

  @action('saveTodo')
  saveTodo(todoId) {
    return function* () {
      const todo = this.todos.get(todoId)
      try {
        yield call(server.saveTodo)({id: todo.id, title: todo.title})
        return dispatch('todoSaved')({id: todo.id, title: todo.title})
      } catch (error) {
        return dispatch('alertUser')(
          'Your todo could not be saved.',
          {
            detailedMessage: error.toString(),
            onRetry: dispatch('saveTodo')(todoId)
          }
        )
      }
    }.bind(this)()
  }

  @action('toggleTodo')
  toggleTodo(todoId) {
    const todo = this.todos.get(todoId)
    todo.isFinished = !todo.isFinished
  }
}

The @action(type) decorator declares that the decorated method responds to the named action type. There is no relationship between the method name and the action type.

Actions

Action handlers may be execute immediately when declared as a regular function or asynchronously when the action handler is defined as a generator function.

Currently, decorators and generator methods are not cooperating syntactically, but this should be resolved as the decorator specification is refined and babel incorporates those improvements. The examples show how you can return a bound generator function until the syntactic convenience is available.

Generator Actions

Asynchronous tasks are supported in generator actions through an inversion of control that allows the store to step through the yield statements of the generator.

Store Context

A StoreContext component is created to share the store with child components.

<StoreContext store={store}>
  <TodoListViewContainer/>
</StoreContext>

Connections

This experiment has as one of its primary goals to enable writing stateless functional ("pure") React components like this one:

class TodoListView extends React.Component {
  handleSubmit = (event) => {
    event.preventDefault()
    this.props.addTodo(this.refs.newTodoTitle.value)
    this.refs.newTodoTitle.value = ''
  }
  render() {
    return (
      <div>
        <ul>
          {this.props.todos.values().map(todo =>
            <TodoView
              key={todo.id}
              title={todo.title}
              isFinished={todo.isFinished}
              onToggle={() => this.props.toggleTodo(todo.id)}
            />
          )}
        </ul>
        Tasks left: {this.props.unfinishedTodoCount}
        <form onSubmit={this.handleSubmit}>
          <label><input ref="newTodoTitle" placeholder="Add todo..." /> <button type="submit">Add</button></label>
        </form>
      </div>
    )
  }
}

Components written like this can be tested independent of the state management framework and used with other state manage frameworks like Redux.

With StoreContext as a parent, child components can be simply and declaratively wrapped with a Connect component that manages the state updates from the Store.

const TodoListViewContainer = connect(
  {
    todos: store => store.state.todoList.todos,
    unfinishedTodoCount: store => store.state.todoList.unfinishedTodoCount,
    toggleTodo: store => store.dispatch('toggleTodo'),
    addTodo: store => store.dispatch('addTodo')
  }
)(TodoListView)

Example

Want to try a working example? Here's how!

npm install
cd examples/basic
npm install
npm start

Note that the package.json files in the examples directory do not contain a full list of actual dependencies, but "inherit" dependencies from main mobx-reactor project. This ensures that all modules import the same MobX modules, otherwise there will be an identity crises.

mobx-reactor's People

Contributors

slider23 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mobx-reactor's Issues

as npm module

Hi @amsb ,
Wouldn't it be nice to have this as an npm module .it does take away the pain of having to configure module loaders other than webpack

Using it in a production app

We are starting developement of a React app and really like the simplistic approach of Mobx. But after working with Redux we also like to have uni-directional data flow in the app. Mobx-reactor is doing just that but unfortunately it's still experimental.

Since we are about to start the development of our app, what is your recommendation? Should we start using mobx-reactor as is? What is the timeline of having reactor production ready?

Or we would be just fine without actions and uni-directional data flow?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.