$ whoami
render = (Data) => UI;
update = (State, Action) => NewState;
Actions = [Update This, Add That, Delete Shit, ...];
AppState = Actions.reduce(update, InitialState);
AppState.subscribe(render);
The only thing that didn't change!
Unlike Flux, Redux does not have the concept of a Dispatcher. This is because it relies on pure functions instead of event emitters, and pure functions are easy to compose and don’t need an additional entity managing them.
const Store = Redux.createStore(...);
Store.dispatch(action);
Store.subscribe(() => ...);
const Dispatcher = new Flux.Dispatcher();
Dispatcher.dispatch(action);
Dispatcher.register(() => ...);
If you’re coming from Flux, there is a single important difference you need to understand. Redux doesn’t have a Dispatcher or support many stores. Instead, there is just a single store with a single root reducing function. As your app grows, instead of adding stores, you split the root reducer into smaller reducers independently operating on the different parts of the state tree.
Redux evolves the ideas of Flux, but avoids its complexity by taking cues from Elm. Whether you have used them or not, Redux only takes a few minutes to get started with.
Horizontal surface area — the number of components in your application layer, component layer, layout layer, rendering layer.
Vertical surface area — the number of layers for a feature.
Verticals are fixed. Horizontal grows with app complexity.
Always remains the same.
npm i --save flux
@2.1.1
import { Dispatcher } from 'flux';
import { Container, ReduceStore } from 'flux/utils';
import { Dispatcher } from 'flux';
export default new Dispatcher();
npm i --save immutable
const Shape = { firstName: 'John', lastName: 'Doe',
age: 0, isActive: true };
class UserRecord extends Record(Shape) {
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
class Users {
async create(data) { ... }
async retrieve(id) { ... }
async update(id, data) { ... }
async destroy(id) { ... }
}
All methods return immutable record defined before.
class Users {
async retrieve(id) {
try {
const response = await get(`/users/${id}`);
const user = await response.json();
return new UserRecord(user);
} catch (error) {
throw new AccessError('Oops');
}
}
}
class UserListStore extends ReduceStore {
getInitialState() {
return new Map();
}
reduce(users, action) {
switch (action.type) {
case USER_RETRIEVED:
return users.set(action.id, action.user);
default:
return users;
}
}
}
export default new UserListStore(Dispatcher);
class UserListStore extends ReduceStore {
// ...
getOnlineUsersCount() {
return this.getState().count(u => u.isOnline);
}
// ...
}
async function retrieveUser(id) {
try {
const user = await users.retrieve(id);
Dispatcher.dispatch({ type: USER_RETRIEVED,
id, user });
} catch (error) {
Dispatcher.dispatch({ type: USER_RETRIEVE_FAILED,
error });
}
}
There should be one— and preferably only one —obvious way to do it.
Special cases aren't special enough to break the rules.
class UserListContainer extends React.Component {
static getStores() {
return [UserListStore];
}
static calculateState() {
const users = UserListStore.getUsers();
const onlineCount = UserListStore.getOnlineUsersCount();
return { users, onlineCount };
}
render() {
return <UserList users={this.state.users}
onlineCount={this.state.onlineCount} />;
}
}
export default Container.create(UserListContainer);
function getStores() {
return [UserListStore];
}
function calculateState() {
const users = UserListStore.getUsers();
const onlineCount = UserListStore.getOnlineUsersCount();
return { users, onlineCount };
}
export default Container.create(UserList,
getStores,
calculateState);
@3.0.0
Link to these slides — alexeyraspopov.github.io/long-live-flux/