
RefluxJS - An Alternative Look At Flux Architecture From Facebook
- Transfer
From a translator: looking at ReactJS and inspired by its simplicity, I began to look for a library that would provide the same simple data exchange inside my application. I came across Flux , saw code examples and went looking for an alternative. I came across RefluxJS, immediately fell in love and went to translate the official dock. It was written just in the style of the article, therefore, in the first place I decided to share it with the Habrasociety. Translation is somewhat free. In some places, if it seemed to me that something needed additional explanation or an example, I was not shy.
In the translation below, the term “event” is sometimes used for the term Action from Reflux, and sometimes the term “action”, depending on the context. I could not find a better translation. If you have options, I am waiting for suggestions in the comments.





RefluxJS is a simple library that provides a unidirectional data stream in your application using Facebook’s Flux concept .
You can read an overview of the Flux architecture here , and this article will describe an alternative option that will use the features of functional programming to a greater extent, moving away from the MVC architecture towards a unidirectional data stream.
The pattern consists of actions and actions stores. Actions initiate data movement through events through storage to visual components. If the user has done something, the corresponding event is generated using the action. A data warehouse is subscribed to this event. It processes the event and, possibly, in turn generates some kind of its own.
For example, a user changed the filtering of a list in an application. The filter component generates a “filter changed” event. The repository responds to this by executing an ajax request with an updated filter and, in turn, informs all those who subscribe to it that the data set supplied to them has changed.
The goal of the RefluxJS project is simpler and faster integration of the Flux architecture into your project, both on the client side and on the server side. However, there are some differences between how RefluxJS works and what classic Flux architecture offers. For more information, see this blog post .
Some RefluxJS concepts are similar to Flux:
RefluxJS is an improved version of the Flux concept, more dynamic and friendlier to functional reactive programming:
Some examples can be found at the following addresses:
At the moment, RefluxJS can be installed using npm or using bower.
To install using npm, run the following command:
To install using bower:
Like React, RefluxJS requires es5-shim for older browsers. You can take it here
A complete example can be found here .
Actions are created by calling `Reflux.createAction ()`. As a parameter, you can pass a list of options.
The action object is a functor , so it can be called by referring to the object as a function:
If `options.sync` is set to true, the event will be triggered as a synchronous operation. You can change this setting at any time. All subsequent calls will use the set value.
To conveniently create a large number of actions, you can do this:
For events that can be processed asynchronously (for example, API calls), there are several different options. In the most general case, we consider successful completion and error. To create various events in this variant, you can use `options.children`.
There is a special option for this case: `options.asyncResult`. The following action definitions are equivalent:
The following methods are available for automatically invoking child actions `completed` and` failed`:
The following three definitions are equivalent:
Asynchronous actions can be used as promises. This is especially convenient for rendering on the server, when you need to wait for the successful (or not) completion of some event before rendering.
Suppose we have an action and storage and we need to execute an API request:
In this case, you can use promises on the server in order to either execute the request and either render something or return an error:
There are several hooks available for each event.
Usage example:
You can define hooks directly when declaring actions:
If you need to be able to execute some method on objects of all actions, for this you can expand the object `Reflux.ActionMethods`, which is automatically mixed with all the actions when it is created.
Usage example:
Repositories are created in much the same way as the ReactJS component classes (`React.createClass`) - by passing an object that defines the parameters of the repository to the` Reflux.createStore` method. All event handlers can be initialized in the repository `init` method by calling the` listenTo` own repository method.
In the example above, when the `statusUpdate` action is called, the` output` storage method will be called with all the parameters passed when sending. For example, if an event was dispatched by calling `statusUpdate (true)`, the `true` flag will be passed to the` output` function. After that, the repository itself will work as an action and pass it `status` as data to its subscribers.
Since the storages themselves are the initiators of event dispatch, they also have the `preEmit` and` shouldEmit` hooks.
If you want to make sure that a certain set of methods is available immediately in all repositories, you can expand the `Reflux.StoreMethods` object, which mixes into all repositories when they are created.
Usage example:
Just as you mix objects into React components, you can mix them with your repositories:
Impurity methods are available in the same way as proprietary methods declared in storage. Therefore, `this` from any method will point to the storage instance:
Conveniently, if several impurities are mixed into the repository that define the same event lifecycle methods (`init`,` preEmit`, `shouldEmit`), all these methods will be guaranteed to be called (as in ReactJS, in fact).
Since usually in the store init method subscribes to all registered actions, the store has a `listenToMany` method, which takes an object with all generated events as an argument. Instead of this code:
... you can use this:
Similar code will add handlers for all `actionName` actions, for which there is a corresponding` onActionName` storage method (or `actionName` if you prefer). In the example above, if the `actions` object also contained the` iceShard` action, it would simply be ignored (since there is no corresponding handler for it).
To make it even more convenient for you, you can assign an object with actions to the property of the storage `listenables` object, it will be automatically transferred to` listenToMany`. Therefore, the example above can be simplified to this:
The `listenables` property can also be an array of similar objects. In this case, each object will be passed to `listenToMany`. This allows you to conveniently do the following:
In your component, you can subscribe to event processing from the repository like this:
We dispatch events in a chain, using the action object `statusUpdate` as a function:
If you do everything as described above, the conclusion should be like this:
You can subscribe to actions in the React component in the ` componentDidMount` [lifecycle method] () method, and unsubscribe in the `componentWillUnmount` method like this:
Since the components must be constantly subscribed / unsubscribed from events at the right moments, for convenience of use, you can use the admixture `Reflux.ListenerMixin`. Using it, the example above can be rewritten like this:
This impurity makes the `listenTo method available for calling inside the component, which works just like the storage method of the same name. You can use the `listenToMany` method.
If you do not use any specific logic regarding `this.listenTo ()` inside `componentDidMount ()`, you can use the call `Reflux.listenTo ()` as an admixture. In this case, `componentDidMount ()` will be automatically configured as required, and you will get an admixture of `ListenerMixin` in your component. Thus, the example above can be rewritten like this:
You can insert multiple calls to `Reflux.listenTo` inside the same` mixins` array.
There is also `Reflux.listenToMany` which works in a similar way, allowing you to use` listener.listenToMany`.
If all you need to do is update the state of the component when receiving data from the repository, you can use the expression `Reflux.connect (listener, [stateKey])` as an admixture of the ReactJS component. If you pass the optional key `stateKey` there, the state of the component will be automatically updated using` this.setState ({: data}) `. If `stateKey` is not passed, a call to` this.setState (data) `will be made. Here is an example above, rewritten to take account of new features:
`Reflux.connectFilter` can be used just like` Reflux.connect`. Use `connectFilter` as an impurity in case you need to pass only a part of the storage state to the component. Let's say a blog written using Reflux is likely to keep all publications in the repository. And on the page of a single post, you can use `Reflux.connectFilter` to filter posts.
A repository can subscribe to changes in other repositories, allowing you to build data transfer chains between repositories to aggregate data without affecting other parts of the application. A repository can subscribe to changes that occur in other repositories using the `listenTo` method, just as it does with action objects:
Don't like the default EventEmitter? You can switch to using any other, including the built-in Node like this:
Don't like the default implementation library of promises? You can switch to using any other (for example, Bluebird like this:
Keep in mind that promises in RefluxJS are created by calling `new Promise (...)`. If your library uses factories, use the call to Reflux.setPromiseFactory ().
Since most libraries do not use constructors to work with promises (`new Promise (...)`), there is no need to set up a factory.
However, if you use something like `Q` or some other library that uses the factory method to create promises, use the` Reflux.setPromiseFactory` call to specify it.
When an action is invoked as a functor, it happens asynchronously. The control is returned immediately, and the corresponding handler is called via `setTimeout` (function` nextTick`) inside RefluxJS.
You can choose the implementation of the deferred method call (`setTimeout`,` nextTick`, `setImmediate`, etc.) that suits you.
As alternatives, better, you may need to Polyphemus `setImmediate` or ` macrotask`
The Reflux API has `join` methods that provide convenient aggregation of sources sending events in parallel. This is the same as the `waitFor` method in Facebook's original Flux implementation.
The handler passed to the corresponding `join ()` call will be called as soon as all participants send the event at least once. The handler will be passed the parameters of each event in the order in which the participants in the operation were declared when calling `join`.
There are four `join` options, each of which is a special data strategy:
The signatures of all methods look the same:
As soon as join () is completed, all restrictions associated with it will be removed and it will be able to work again if the publishers send events to the chain again.
All objects that use the listener API (storages, React components that mix with ListenerMixin, or other components that use ListenerMethods) access the four options of the join method that we talked about above:
Since using the join methods and then sending the event to the chain is a common thing for the repository, all join methods have their static equivalents in the Reflux object, which return the repository object subscribed to the specified events. Using these methods, the example above can be rewritten as follows:
The `listenTo` function provided by the repository and` ListenerMixin` has a third parameter, which can be a function. This function will be called at the time of registering the handler with the result of calling `getInitialState` as parameters.
Remember the `listenToMany` method? If you use it with other repositories, it also supports `getInitialState`. The data returned by this method will be passed to the regular handler, or to the `this.onDefault` method, if it exists.
In the translation below, the term “event” is sometimes used for the term Action from Reflux, and sometimes the term “action”, depending on the context. I could not find a better translation. If you have options, I am waiting for suggestions in the comments.
Overview
RefluxJS is a simple library that provides a unidirectional data stream in your application using Facebook’s Flux concept .
You can read an overview of the Flux architecture here , and this article will describe an alternative option that will use the features of functional programming to a greater extent, moving away from the MVC architecture towards a unidirectional data stream.
╔═════════╗ ╔════════╗ ╔═════════════════╗
║ Actions ║──────>║ Stores ║──────>║ View Components ║
╚═════════╝ ╚════════╝ ╚═════════════════╝
^ │
└──────────────────────────────────────┘
The pattern consists of actions and actions stores. Actions initiate data movement through events through storage to visual components. If the user has done something, the corresponding event is generated using the action. A data warehouse is subscribed to this event. It processes the event and, possibly, in turn generates some kind of its own.
For example, a user changed the filtering of a list in an application. The filter component generates a “filter changed” event. The repository responds to this by executing an ajax request with an updated filter and, in turn, informs all those who subscribe to it that the data set supplied to them has changed.
Content
- Comparison of Reflux and Facebook Flux
- Examples
- Installation
- Using
- Events
- Storage facilities
- Use with ReactJS Components
- Details
- Epilogue
Comparison of Reflux and Facebook Flux
The goal of the RefluxJS project is simpler and faster integration of the Flux architecture into your project, both on the client side and on the server side. However, there are some differences between how RefluxJS works and what classic Flux architecture offers. For more information, see this blog post .
Similarities to Flux
Some RefluxJS concepts are similar to Flux:
- There are actions
- There are data stores
- Data moves in only one direction.
Differences from Flux
RefluxJS is an improved version of the Flux concept, more dynamic and friendlier to functional reactive programming:
- The dispatcher, which was a singleton in Flux, is not in RefluxJS. Instead, each event (action) is its own dispatcher.
- Since you can subscribe to actions, stores can do this directly without using cumbersome switch statements to separate flies from cutlets.
- Vaults can subscribe to other vaults. That is, it becomes possible to create repositories that aggregate and process map-reduce data.
- The call to waitFor () has been deleted. Instead, data processing can be performed sequentially or in parallel.
- Repositories that aggregate data (see above) can subscribe to other repositories, processing messages sequentially
- To wait for other events to be processed, you can use the join () method
- Special action factories are not needed at all, since RefluxJS actions are functions that transmit the necessary data to everyone who subscribes to them.
Examples
Some examples can be found at the following addresses:
- An example of developing a draft to-do list on RefluxJS
- HackerNews Clone Owned by echenley
- Another TODO project using python to implement the backend by limelights
Installation
At the moment, RefluxJS can be installed using npm or using bower.
NPM
To install using npm, run the following command:
npm install reflux
Bower
To install using bower:
bower install reflux
ES5
Like React, RefluxJS requires es5-shim for older browsers. You can take it here
Using
A complete example can be found here .
Create action
Actions are created by calling `Reflux.createAction ()`. As a parameter, you can pass a list of options.
var statusUpdate = Reflux.createAction(options);
The action object is a functor , so it can be called by referring to the object as a function:
statusUpdate(data); // Вызываем экшен statusUpdate, передавая в качестве данных data
statusUpdate.triggerAsync(data); // Тоже самое, что выше
If `options.sync` is set to true, the event will be triggered as a synchronous operation. You can change this setting at any time. All subsequent calls will use the set value.
To conveniently create a large number of actions, you can do this:
var Actions = Reflux.createActions([
"statusUpdate",
"statusEdited",
"statusAdded"
]);
// Теперь объект Actions содержит экшены с именами, которые мы передали в вызов createActions().
// Инициировать события можно как обычно
Actions.statusUpdate();
Asynchronous work with actions
For events that can be processed asynchronously (for example, API calls), there are several different options. In the most general case, we consider successful completion and error. To create various events in this variant, you can use `options.children`.
// Создаем экшены 'load', 'load.completed' и 'load.failed'
var Actions = Reflux.createActions({
"load": {children: ["completed","failed"]}
});
// При получении данных от экшена 'load', асинхронно выполняем операцию,
// а затем в зависимости от результата, вызываем экшены failed или completed
Actions.load.listen( function() {
// По умолчанию обработчик привязан к событию.
//Поэтому его дочерние элементы доступны через this
someAsyncOperation()
.then( this.completed )
.catch( this.failed );
});
There is a special option for this case: `options.asyncResult`. The following action definitions are equivalent:
createAction({
children: ["progressed","completed","failed"]
});
createAction({
asyncResult: true,
children: ["progressed"]
});
The following methods are available for automatically invoking child actions `completed` and` failed`:
- `promise` - As a parameter, it expects a promise object and binds the` completed` and `failed` call to this promise using` then () `and` catch () `.
- `listenAndPromise` - As a parameter, it expects a function that returns a promise object. It (the promise object that the function returned) will be called when the event occurs. Accordingly, by `then ()` and `catch ()` of the promise, completed and failed are automatically called.
The following three definitions are equivalent:
asyncResultAction.listen( function(arguments) {
someAsyncOperation(arguments)
.then(asyncResultAction.completed)
.catch(asyncResultAction.failed);
});
asyncResultAction.listen( function(arguments) {
asyncResultAction.promise( someAsyncOperation(arguments) );
});
asyncResultAction.listenAndPromise( someAsyncOperation );
Asynchronous Actions as Promises
Asynchronous actions can be used as promises. This is especially convenient for rendering on the server, when you need to wait for the successful (or not) completion of some event before rendering.
Suppose we have an action and storage and we need to execute an API request:
// Создаем асинхронный экшен с `completed` & `failed` "подэкшенами"
var makeRequest = Reflux.createAction({ asyncResult: true });
var RequestStore = Reflux.createStore({
init: function() {
this.listenTo(makeRequest, 'onMakeRequest');
},
onMakeRequest: function(url) {
// Предположим, что `request` - какая-то HTTP библиотека
request(url, function(response) {
if (response.ok) {
makeRequest.completed(response.body);
} else {
makeRequest.failed(response.error);
}
})
}
});
In this case, you can use promises on the server in order to either execute the request and either render something or return an error:
makeRequest('/api/something').then(function(body) {
// Render the response body
}).catch(function(err) {
// Handle the API error object
});
Event Hooks
There are several hooks available for each event.
- `preEmit` - is called before the action passes event information to subscribers. As arguments, the hook receives the arguments used to dispatch the event. If the hook returns anything other than undefined, the return value will be used as parameters for the shouldEmit hook and will replace the data sent
- `shouldEmit` - called after` preEmit`, but before the action passes event information to subscribers. By default, this handler returns true, which allows sending data. You can override this behavior, for example, to check the arguments and decide whether the event should be sent to the chain or not.
Usage example:
Actions.statusUpdate.preEmit = function() { console.log(arguments); };
Actions.statusUpdate.shouldEmit = function(value) {
return value > 0;
};
Actions.statusUpdate(0);
Actions.statusUpdate(1);
// Должно быть выведено: 1
You can define hooks directly when declaring actions:
var action = Reflux.createAction({
preEmit: function(){...},
shouldEmit: function(){...}
});
Reflux.ActionMethods
If you need to be able to execute some method on objects of all actions, for this you can expand the object `Reflux.ActionMethods`, which is automatically mixed with all the actions when it is created.
Usage example:
Reflux.ActionMethods.exampleMethod = function() { console.log(arguments); };
Actions.statusUpdate.exampleMethod('arg1');
// Выведет: 'arg1'
Storage Creation
Repositories are created in much the same way as the ReactJS component classes (`React.createClass`) - by passing an object that defines the parameters of the repository to the` Reflux.createStore` method. All event handlers can be initialized in the repository `init` method by calling the` listenTo` own repository method.
// Создаем хранилище
var statusStore = Reflux.createStore({
// Начальная настройка
init: function() {
// Подписываемся на экшен statusUpdate
this.listenTo(statusUpdate, this.output);
},
// Определяем сам обработчик события, отправляемого экшеном
output: function(flag) {
var status = flag ? 'ONLINE' : 'OFFLINE';
// Используем хранилище как источник события, передавая статус как данные
this.trigger(status);
}
});
In the example above, when the `statusUpdate` action is called, the` output` storage method will be called with all the parameters passed when sending. For example, if an event was dispatched by calling `statusUpdate (true)`, the `true` flag will be passed to the` output` function. After that, the repository itself will work as an action and pass it `status` as data to its subscribers.
Since the storages themselves are the initiators of event dispatch, they also have the `preEmit` and` shouldEmit` hooks.
Reflux.StoreMethods
If you want to make sure that a certain set of methods is available immediately in all repositories, you can expand the `Reflux.StoreMethods` object, which mixes into all repositories when they are created.
Usage example:
Reflux.StoreMethods.exampleMethod = function() { console.log(arguments); };
statusStore.exampleMethod('arg1');
// Будет выведено: 'arg1'
Impurities (mixins) in storage
Just as you mix objects into React components, you can mix them with your repositories:
var MyMixin = { foo: function() { console.log('bar!'); } }
var Store = Reflux.createStore({
mixins: [MyMixin]
});
Store.foo(); // Выведет "bar!" в консоль
Impurity methods are available in the same way as proprietary methods declared in storage. Therefore, `this` from any method will point to the storage instance:
var MyMixin = { mixinMethod: function() { console.log(this.foo); } }
var Store = Reflux.createStore({
mixins: [MyMixin],
foo: 'bar!',
storeMethod: function() {
this.mixinMethod(); // Выведет "bar!"
}
});
Conveniently, if several impurities are mixed into the repository that define the same event lifecycle methods (`init`,` preEmit`, `shouldEmit`), all these methods will be guaranteed to be called (as in ReactJS, in fact).
Convenient subscription to a large number of actions
Since usually in the store init method subscribes to all registered actions, the store has a `listenToMany` method, which takes an object with all generated events as an argument. Instead of this code:
var actions = Reflux.createActions(["fireBall","magicMissile"]);
var Store = Reflux.createStore({
init: function() {
this.listenTo(actions.fireBall,this.onFireBall);
this.listenTo(actions.magicMissile,this.onMagicMissile);
},
onFireBall: function(){
// whoooosh!
},
onMagicMissile: function(){
// bzzzzapp!
}
});
... you can use this:
var actions = Reflux.createActions(["fireBall","magicMissile"]);
var Store = Reflux.createStore({
init: function() {
this.listenToMany(actions);
},
onFireBall: function(){
// whoooosh!
},
onMagicMissile: function(){
// bzzzzapp!
}
});
Similar code will add handlers for all `actionName` actions, for which there is a corresponding` onActionName` storage method (or `actionName` if you prefer). In the example above, if the `actions` object also contained the` iceShard` action, it would simply be ignored (since there is no corresponding handler for it).
Property `listenables`
To make it even more convenient for you, you can assign an object with actions to the property of the storage `listenables` object, it will be automatically transferred to` listenToMany`. Therefore, the example above can be simplified to this:
var actions = Reflux.createActions(["fireBall","magicMissile"]);
var Store = Reflux.createStore({
listenables: actions,
onFireBall: function(){
// whoooosh!
},
onMagicMissile: function(){
// bzzzzapp!
}
});
The `listenables` property can also be an array of similar objects. In this case, each object will be passed to `listenToMany`. This allows you to conveniently do the following:
var Store = Reflux.createStore({
listenables: [require('./darkspells'),require('./lightspells'),{healthChange:require('./healthstore')}],
// остальной код удален для улучшения читаемости
});
Subscribing to repositories (handling events sent by repositories)
In your component, you can subscribe to event processing from the repository like this:
// Хранилище данных для статуса
var statusStore = Reflux.createStore({
// Начальная настройка
init: function() {
// Подписываемся на экшен statusUpdate
this.listenTo(statusUpdate, this.output);
},
// Обработчик
output: function(flag) {
var status = flag ? 'ONLINE' : 'OFFLINE';
// Инициируем собственное событие
this.trigger(status);
}
});
// Очень простой компонент, который просто выводит данные в консоль
function ConsoleComponent() {
// Регистрируем обработчик протоколирования
statusStore.listen(function(status) {
console.log('status: ', status);
});
};
var consoleComponent = new ConsoleComponent();
We dispatch events in a chain, using the action object `statusUpdate` as a function:
statusUpdate(true);
statusUpdate(false);
If you do everything as described above, the conclusion should be like this:
status: ONLINE status: OFFLINE
An example of working with React components
You can subscribe to actions in the React component in the ` componentDidMount` [lifecycle method] () method, and unsubscribe in the `componentWillUnmount` method like this:
var Status = React.createClass({
initialize: function() { },
onStatusChange: function(status) {
this.setState({
currentStatus: status
});
},
componentDidMount: function() {
this.unsubscribe = statusStore.listen(this.onStatusChange);
},
componentWillUnmount: function() {
this.unsubscribe();
},
render: function() {
// Рендеринг компонента
}
});
Impurities for convenient work inside React components
Since the components must be constantly subscribed / unsubscribed from events at the right moments, for convenience of use, you can use the admixture `Reflux.ListenerMixin`. Using it, the example above can be rewritten like this:
var Status = React.createClass({
mixins: [Reflux.ListenerMixin],
onStatusChange: function(status) {
this.setState({
currentStatus: status
});
},
componentDidMount: function() {
this.listenTo(statusStore, this.onStatusChange);
},
render: function() {
// render specifics
}
});
This impurity makes the `listenTo method available for calling inside the component, which works just like the storage method of the same name. You can use the `listenToMany` method.
Using Reflux.listenTo
If you do not use any specific logic regarding `this.listenTo ()` inside `componentDidMount ()`, you can use the call `Reflux.listenTo ()` as an admixture. In this case, `componentDidMount ()` will be automatically configured as required, and you will get an admixture of `ListenerMixin` in your component. Thus, the example above can be rewritten like this:
var Status = React.createClass({
mixins: [Reflux.listenTo(statusStore,"onStatusChange")],
onStatusChange: function(status) {
this.setState({
currentStatus: status
});
},
render: function() {
// Рендеринг с использованием `this.state.currentStatus`
}
});
You can insert multiple calls to `Reflux.listenTo` inside the same` mixins` array.
There is also `Reflux.listenToMany` which works in a similar way, allowing you to use` listener.listenToMany`.
Using Reflux.connect
If all you need to do is update the state of the component when receiving data from the repository, you can use the expression `Reflux.connect (listener, [stateKey])` as an admixture of the ReactJS component. If you pass the optional key `stateKey` there, the state of the component will be automatically updated using` this.setState ({: data}) `. If `stateKey` is not passed, a call to` this.setState (data) `will be made. Here is an example above, rewritten to take account of new features:
var Status = React.createClass({
mixins: [Reflux.connect(statusStore,"currentStatus")],
render: function() {
// render using `this.state.currentStatus`
}
});
Using Reflux.connectFilter
`Reflux.connectFilter` can be used just like` Reflux.connect`. Use `connectFilter` as an impurity in case you need to pass only a part of the storage state to the component. Let's say a blog written using Reflux is likely to keep all publications in the repository. And on the page of a single post, you can use `Reflux.connectFilter` to filter posts.
var PostView = React.createClass({
mixins: [Reflux.connectFilter(postStore,"post", function(posts) {
posts.filter(function(post) {
post.id === this.props.id;
});
})],
render: function() {
// Отрисовываем, используя `this.state.post`
}
});
Handling change events from other repositories
A repository can subscribe to changes in other repositories, allowing you to build data transfer chains between repositories to aggregate data without affecting other parts of the application. A repository can subscribe to changes that occur in other repositories using the `listenTo` method, just as it does with action objects:
// Создаем хранилище, которое реагирует на изменения, происходящие в statusStore
var statusHistoryStore = Reflux.createStore({
init: function() {
// Подписываемся на хранилище как на экшен
this.listenTo(statusStore, this.output);
this.history = [];
},
// Обработчик экшена
output: function(statusString) {
this.history.push({
date: new Date(),
status: statusString
});
// Инициируем собственное событие
this.trigger(this.history);
}
});
Additional features
Using an alternative event management library
Don't like the default EventEmitter? You can switch to using any other, including the built-in Node like this:
// Это нужно сделать до создания экшенов и хранилищ
Reflux.setEventEmitter(require('events').EventEmitter);
Using an Alternative Promise Library
Don't like the default implementation library of promises? You can switch to using any other (for example, Bluebird like this:
// Это нужно сделать до вызова любых экшенов
Reflux.setPromise(require('bluebird'));
Keep in mind that promises in RefluxJS are created by calling `new Promise (...)`. If your library uses factories, use the call to Reflux.setPromiseFactory ().
Using Promises Factory
Since most libraries do not use constructors to work with promises (`new Promise (...)`), there is no need to set up a factory.
However, if you use something like `Q` or some other library that uses the factory method to create promises, use the` Reflux.setPromiseFactory` call to specify it.
// Это нужно сделать до использования экшенов
Reflux.setPromiseFactory(require('Q').Promise);
Using nextTick Alternative
When an action is invoked as a functor, it happens asynchronously. The control is returned immediately, and the corresponding handler is called via `setTimeout` (function` nextTick`) inside RefluxJS.
You can choose the implementation of the deferred method call (`setTimeout`,` nextTick`, `setImmediate`, etc.) that suits you.
// node.js env
Reflux.nextTick(process.nextTick);
As alternatives, better, you may need to Polyphemus `setImmediate` or ` macrotask`
Waiting for all actions in the chain to complete
The Reflux API has `join` methods that provide convenient aggregation of sources sending events in parallel. This is the same as the `waitFor` method in Facebook's original Flux implementation.
Tracking Arguments
The handler passed to the corresponding `join ()` call will be called as soon as all participants send the event at least once. The handler will be passed the parameters of each event in the order in which the participants in the operation were declared when calling `join`.
There are four `join` options, each of which is a special data strategy:
- `joinLeading`: Only the result of the first event call is saved from each publisher. All other data is ignored.
- joinTrailing: Only the result of the last event call is saved from each publisher. All other data is ignored.
- `joinConcat`: All results are stored in an array.
- joinStrict: Recalling an event from the same publisher results in an error.
The signatures of all methods look the same:
joinXyz(...publisher, callback)
As soon as join () is completed, all restrictions associated with it will be removed and it will be able to work again if the publishers send events to the chain again.
Using instance methods for event management
All objects that use the listener API (storages, React components that mix with ListenerMixin, or other components that use ListenerMethods) access the four options of the join method that we talked about above:
var gainHeroBadgeStore = Reflux.createStore({
init: function() {
this.joinTrailing(actions.disarmBomb, actions.saveHostage, actions.recoverData, this.triggerAsync);
}
});
actions.disarmBomb("warehouse");
actions.recoverData("seedyletter");
actions.disarmBomb("docks");
actions.saveHostage("offices",3);
// `gainHeroBadgeStore` в этом месте кода хранилище отправит событие в цепочку с параметрами `[["docks"],["offices",3],["seedyletter"]]`
Using static methods
Since using the join methods and then sending the event to the chain is a common thing for the repository, all join methods have their static equivalents in the Reflux object, which return the repository object subscribed to the specified events. Using these methods, the example above can be rewritten as follows:
var gainHeroBadgeStore = Reflux.joinTrailing(actions.disarmBomb, actions.saveHostage, actions.recoverData);
Sending default state using listenTo method
The `listenTo` function provided by the repository and` ListenerMixin` has a third parameter, which can be a function. This function will be called at the time of registering the handler with the result of calling `getInitialState` as parameters.
var exampleStore = Reflux.createStore({
init: function() {},
getInitialState: function() {
return "какие-то данные по умолчанию";
}
});
// Подписываемся на события от хранилища
this.listenTo(exampleStore, onChangeCallback, initialCallback)
// initialCallback будет вызван немедленно с параметром "какие-то данные по умолчанию"
Remember the `listenToMany` method? If you use it with other repositories, it also supports `getInitialState`. The data returned by this method will be passed to the regular handler, or to the `this.onDefault` method, if it exists.
Colophon
- List of Contributors
- Project License: BSD 3-Clause License . Copyright 2014, Mikael Brassman.
- License Details
- Reflux uses eventemitter3 , which is currently licensed under MIT