There is interesting article
which describes 4 main classes exposed in Flux
Utils.
3.0.0
)But it's not super clear what should be used for certain situations. There are only 2 examples for ReduceStore
and Container
, but no samples for others unfortunately.
Could you please explain basic usage for these 4 components: when and where they should be used in real life?
Extended answers and code examples would be really appreciated!
UPDATE:
MapStore has been removed starting from 3.0.0
By poking through the code and reading through the method documentation, here's what I can work out (I have not used these classes myself, as I use other Flux frameworks).
It's actually useful to go in almost reverse order for these.
This is not a subclass of FluxStore
because it is, unsurprisingly, not a store. The Container
is a wrapper class for your React UI components that automatically pulls state from specified stores.
For example, if I have a React-driven chat app with a component that lists all my logged-in friends, I probably want to have it pull state from a LoggedInUsersStore
, which would hypothetically be an array of these users.
My component would look something like this (derived from the code example they provide):
import {Component} from 'react';
import {Container} from 'flux/utils';
import {LoggedInUsersStore} from /* somewhere */;
import {UserListUI} from /* somewhere */;
class UserListContainer extends Component {
static getStores() {
return [UsersStore];
}
static calculateState(prevState) {
return {
loggedInUsers: LoggedInUsersStore.getState(),
};
}
render() {
return <UserListUI counter={this.state.counter} />;
}
}
const container = Container.create(UserListContainer);
This wrapper automatically updates the component's state if its registered stores change state, and it does so efficiently by ignoring any other changes (i.e. it assumes that the component does not depend on other parts of the application state).
I believe this is a fairly direct extension of Facebook's React coding principles, in which every bit of UI lives in a high-level "Container." Hence the name.
A ReduceStore
is a store based entirely on pure functions---functions that are deterministic on their inputs (so the same function always returns the same thing for the same input) and produce no observable side effects (so they don't affect other parts of the code).
For example, the lambda (a) => { return a * a; }
is pure: it is deterministic and has no side effects. (a) => { echo a; return a; }
is impure: it has a side effect (printing a
). (a) => { return Math.random(); }
is impure: it is nondeterministic.
The goal with a ReduceStore
is simplification: by making your store is pure, you can make certain assumptions. Because the reductions are deterministic, anyone can perform the reductions at any time and get the same result, so sending a stream of actions is all but identical to sending raw data. Likewise, sending the raw data is perfectly reasonable because you were guaranteed no side effects: if my entire program is made of ReduceStore
s, and I overwrite the state of one client with the state of another (calling the required redraws), I am guaranteed perfect functionality. Nothing in my program can change because of the actions rather than the data.
Anyway, a ReduceStore
should only implement the methods explicitly listed in its documentation. getInitialState()
should determine the initial state, reduce(state, action)
should transform state
given action
(and not use this
at all: that would be non-deterministic/have side effects), and getState()
& areEqual(one,two)
should handle separating the raw state from the returned state (so that the user can't accidentally modify it).
For example, a counter would be a sensible ReduceStore
:
class TodoStore extends ReduceStore {
getInitialState() {
return 0;
}
reduce(state, action) {
switch(action.type) {
case 'increment':
return state + 1;
case 'decrement':
return state - 1;
case 'reset':
return 0;
default:
return state;
}
getState() {
// return `this._state`, which is that one number, in a way that doesn't let the user modify it through something like `store.getState() = 5`
// my offhand JS knowledge doens't let me answer that with certainty, but maybe:
var a = this._state + 1;
return a - 1;
}
}
Notice that none of the transforms explicitly depended on the current state of the object: they only operated on the state
variable they were passed. This means that an instance of store can calculate state for another instance of the same store. Not so useful in the current implementation of FB Flux, but still.
Note: this class does not ensure that your code is purely-functional. My guess is that it will break if you don't check that yourself.
I would always use this store. Unless I could use a...
This class is no longer part of Flux!
This is a subclass of ReduceStore
. It is for such pure-functional stores that happen to be Maps internally. Specifically, Immutable.JS maps (another FB thing!).
They have convenience methods to get keys and values from the state:
WarrantiesStore.at('extended')
rather than WarrantiesStore.getState().get('extended')
.
This brings us to FluxStore: the catch-all Store class and generic implementation of the Flux Store concept.
The other two stores are its descendants.
The documentation seems to me to be fairly clear on its usage, so I'll leave it at that
Store
util classes to hold your dataIn my case, that would be never: I prefer immutable frameworks like redux and NuclearJS because they are easier for me to reason about. I take care to structure my stores in a purely functional way. But if you don't, this class is good.