0.7.0 • Published 9 years ago

flax v0.7.0

Weekly downloads
3
License
MIT
Repository
github
Last release
9 years ago

Flax

A productive flux implementation to be used with ReactJS

Flax is based on Flux

Goals

  • Productivity, objectivity and clarity
  • Maintain as much freedom as traditional Flux
  • Common flux problems solution proposals

Why Flax?

Flax is being developed to address professional level challenges with:

  • Clarity: Flax lets you write Flux in a similar way you would write an app with ReactJS, making it intuitive to learn;
  • Productivity: Flax reduces the common boilerplate while not losing granularity such as the publish-subscribe stores' pattern, but still making simpler syntax possible;
  • Seamlessness: with Flax, for instance, there is little difference between sync and async actions, making action creators easy to implement without additional middleware;
  • Pattern-oriented: alongside with the docs, this repository is being built to also show the best unidirectional dataflow practices to be used with Flax;
  • Developer tools: Flax is being designed to support the creation of user-defined development tools. See flax-devtools, as an example;

Usage

Actions

import Flax from 'flax';

const AppActions = Flax.createActionCreator({
  displayName: 'AppActions',

  // Action with empty payload
  action1() {
    // ...
    this.dispatch();
  },
  
  // Action with some payload
  action2(x, y) {
    x++;
    y++;
    this.dispatch({x, y});
  },
  
  // Async action
  
  /*
  *  We suggest all async requests to be done inside action creators as a form of keeping the stores sync
  */
  action3(x) {
    this.dispatch({loading: true});
    fecth('<url>').then(response => {
      let data = response.json() + x;
      this.dispatch({
        loading: false,
        data
      });
      this.resolve(); // action finished successful
    }).catch(err => {
      this.dispatch({
        loading: false,
        err
      });
      this.reject(); // action finished with failure
    });
  }
});

export default AppActions;

Stores

import Flax from 'flax';
import AppActions from '../actions/AppActions.jsx';

const AppStore = Flax.createStore({
  displayName: 'AppStore',

  getInitialState() {
    return {
      num1: 0,
      num2: 0,
      loading: false,
      error: null
    };
  },

  getActionBinds() {
    return [
      [AppActions.action1, this._handleAction1],
      [AppActions.action2, this._handleAction2],
      [AppActions.action3, this._handleAction3]
    ];
  },

  events: {
    NUM1: null,
    NUM2: null,
    LOADING: null,
    ERROR: null
  },

  _handleAction1() {
    this.state.num1 = 0;
    this.state.num2 = 0;
    this.emitChange(this.NUM1);
    this.emitChange(this.NUM2);
  },

  _handleAction2(payload) {
    this.state.num1 = payload.x;
    this.state.num2 = payload.y;
    this.emitChange(this.NUM1);
    this.emitChange(this.NUM2);
  },

  _handleAction3({loading, data, err}) {
    this.state.loading = false;
    if (err) return this.emitChange(this.ERROR);
    if (loading) {
      this.state.loading = true;
      this.emitChange(this.LOADING);
    } else {
      this.state.num1 = data;
      this.emitChange(this.NUM1);
    }
  },

  getters: {
    getNum1() {
      return this.state.num1;
    },

    getNum2() {
      return this.state.num2;
    },

    getLoading() {
      return this.state.loading;
    },

    getError() {
      return this.state.error;
    }
  }
  
});

export default AppStore;

View Controller

import React from 'react';
import AppActions from '../actions/AppActions.jsx';
import AppStore from '../stores/AppStore.js';

import {
  StoreWatcher
} from 'flax';

const App = React.createClass({
  mixins: [StoreWatcher],

  getEventBinds() {
    return [
      [AppStore.NUM1, this._handleNum1],
      [AppStore.NUM2, this._handleNum2],
      [AppStore.LOADING, this._handleLoading],
      [AppStore.ERROR, this._handleError]
    ];
  },

  getInitialState() {
    return {
      num1: AppStore.getNum1(),
      num2: AppStore.getNum2(),
      loading: AppStore.getLoading(),
      error: AppStore.getError()
    };
  },

  _handleNum1() {
    this.setState({
      num1: AppStore.getNum1()
    });
  },

  _handleNum2() {
    this.setState({
      num2: AppStore.getNum2()
    });
  },

  _handleLoading() {
    this.setState({
      loading: AppStore.getLoading()
    });
  },

  _handleError() {
    this.setState({
      error: AppStore.getError()
    });
  },

  _handleAction1() {
    AppActions.action1();
  },

  _handleAction2() {
    AppActions.action2(1, 2);
  },

  _handleAction3() {
    // Action chaining is important to wait for async action
    AppActions.action3(10).then(() => {
      AppActions.action1();
      AppActions.action2(-1, -2);
    }).catch(() => {
      AppActions.action2(0, 0);
    });
  },

  render() {
    let {num1, num2, loading, error} = this.state;
    
    return (
      <div>
        {loading ? <p>Loading...</p> : null}
        {error ? <p>{error}</p> : null}
        <button onClick={this._handleAction1}>Action1</button>
        <button onClick={this._handleAction2}>Action2</button>
        <button onClick={this._handleAction3}>Action3</button>
        <p>{num1}</p>
        <p>{num2}</p>
      </div>
    );
  }
});

export default App;

getInitialState could be:

getInitialState() {
  let {num1, num2, loading, error} = AppStore.getState();
  return {num1, num2, loading, error};
},

Or in a cleaner code:

getInitialState() {
  return AppStore.getState();
},

If you want less granularity, the component could be set to listen to all AppStore's events:

getEventBinds() {
  return [
    [AppStore.DEFAULT, this._handleAppChange]
  ];
},

And the store event handles:

_handleAppChange() {
  this.setState(AppStore.getState());
},

License

MIT

Contributors

0.7.0

9 years ago

0.6.1

9 years ago

0.6.0

9 years ago

0.5.0

9 years ago

0.4.0

10 years ago

0.3.1

10 years ago

0.3.0

10 years ago

0.2.0

10 years ago

0.1.1

10 years ago

0.1.0

10 years ago

0.0.1

10 years ago

0.0.0

13 years ago