LoginSignup
5
5

More than 5 years have passed since last update.

Modern React Flux application example (pt. 2)

Last updated at Posted at 2014-10-29

Continuing from where we left off in part 1, I want to do three things here:

  1. Set up project for Flux work

  2. Have a simple build process

  3. Babby's first create entity

Project setup

Standard directory structure goes something like this:

src/
  app.js -- the entry point of your application and build target

  actions/ -- the actions used by your views
    ExampleActions.js

  components/ -- you can name this "views", your React components
    ExampleApp.js
    ExampleElement.js
    ExampleRow.js
    ExampleTable.js

  constants/ -- your constants used to communicate between your actions and stores
    ExampleConsants.js

  dispatchers/ -- your dispatcher(s) for handling actions
    ExampleDispatcher.js

  stores/ -- your store
    ExampleStore.js

In reality, you can choose to not use constants, but I like not having to depend on the action not having to coordinate the exact matching string ID with the store (also helps code analysis tools and whatnot).

Nothing too special in the code here. See the example repo for code details.

Build process

I like using Gulp to run tasks and Webpack to actually build my application. I use Pete Hunt's jsx-loader to handle loading my JS files and transforming JSX as needed.

I also like to use aliases to make requiring modules easier, and avoid using relative paths for things I need in multiple places. Relevant excerpt:

resolve: {
  alias: {
    ExampleStore$: path.join(__dirname, 'src/stores/ExampleStore.js'),
    ExampleDispatcher$: path.join(__dirname, 'src/dispatchers/ExampleDispatcher.js'),
    ExampleConstants$: path.join(__dirname, 'src/constants/ExampleConstants.js'),
    ExampleActions$: path.join(__dirname, 'src/actions/ExampleActions.js'),
    propsEqual$: path.join(__dirname, 'src/utils/propsEqual.js')
  }
}

See example repo for my setup.

Create an entity through Flux

Here's all of the code required to do this:

constants/ExampleConstants.js:

var keyMirror = require('react/lib/keyMirror');

module.exports = keyMirror({
  CREATE_ROW: null
  // add more constants for more actions
});

actions/ExampleActions.js:

var ExampleDispatcher = require('ExampleDispatcher');
var ExampleConstants = require('ExampleConstants');

module.exports = {
  createRow: function (content) {
    ExampleDispatcher.handleViewAction({
      actionType: ExampleConstants.CREATE_ROW,
      content: content
    });
  }
  // add more methods likewise
};

dispatchers/ExampleDispatcher.js:

var copyProperties = require('react/lib/copyProperties');
var Dispatcher = require('flux').Dispatcher;

module.exports = copyProperties(new Dispatcher, {
  handleViewAction: function (action) {
    this.dispatch({
      source: 'VIEW_ACTION',
      action: action
    });
  }
  // add more methods for other action sources like the server
});

stores/ExampleStore.js:

var ExampleDispatcher = require('ExampleDispatcher');
var EventEmitter = require('events').EventEmitter;
var ExampleConstants = require('ExampleConstants');
var merge = require('react/lib/merge');

var CHANGE_EVENT = 'change';

var _appState = {
  data: []
};

function generateRandom() {
  return (Math.random() * Date.now() | 0).toString(36);
}

function createRow(content) {
  _appState.data.push({
    id: generateRandom(),
    content: content,
    metadata: [
      generateRandom(),
      generateRandom(),
      generateRandom(),
      generateRandom(),
      generateRandom(),
      generateRandom(),
      generateRandom(),
      generateRandom(),
      generateRandom()
    ]
  });
}

var ExampleStore = merge(EventEmitter.prototype, {
  emitChange: function () {
    this.emit(CHANGE_EVENT);
  },

  addChangeListener: function (callback) {
    this.on(CHANGE_EVENT, callback);
  },

  removeChangeListener: function (callback) {
    this.removeListener(CHANGE_EVENT, callback);
  },

  getAppState: function () {
    return _appState;
  },

  dispatcherIndex: ExampleDispatcher.register(function (payload) {
    var action = payload.action;

    switch (action.actionType) {
      case ExampleConstants.CREATE_ROW: {
        createRow(action.content);
        ExampleStore.emitChange();
        break;
      }
      // likewise, create more cases to handle other constants & actions
    }

    return true; // return promise
  })
});

module.exports = ExampleStore;

So as you can see, there's not too much code involved to do this. Just some basic pieces that you need to put together.

And so, call the action method whenever you need to create rows.

// more stuff
  _createRow: function () {
    ExampleActions.createRow((Math.random() * 1e10 | 0).toString(36));
  },

  render: function () {
    if (!this.state) {
      return null;
    }
    return (
      <div className='example-app'>
        <h1>This is an example app</h1>
        <button
          className="example-button"
          onClick={this._createRow}>
          <span className="example-button-span">
            Create random row
          </span>
        </button>
        <ExampleTable
          data={this.state.data}
        />
      </div>
    );
// more stuff

What next?

Part 3

Pure Render Mixin and "pure" components

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5