Cruiser
Cruiser is a way of managing the state of your front-end application in a fashion similar to popular patterns like Facebook's Flux or Redux. Cruiser follows the principle of keeping your application's state in a single, immutable tree that is mutated via "actions". But where actions in Flux or Redux are dispatched as object literals that mutate the data in a "reducer" function, Cruiser opts to omit the middleman by making the action itself a reducer.
Some additional features about Cruiser...
- Written in Typescript and actions support explicit type-safety constraints.
- All operations are ordered and atomic.
- Updates to subscribers can be batched, allowing for a reduction in unnecessary operations.
- Provides bindings for React.
- Extensively commented throughout, allowing for documentation via intellisense.
Getting Started
Install cruiser
via npm
:
npm install cruiser
Usage Example
Note: All examples provided use Typescript, though you can still use the library without it.
The first step is to create a store that will contain the "model" for your application. Though the model for a store can be any type, it is often preferable to use an object literal. Additionally, Typescript users will want to create a type
contract for their Model
that can be used to enforce type-safety across their various actions.
One you have a store, you can add functions to listen for state changes using the .subscribe()
method on the created store. For our example, we'll just output the contents of our store as a console log.
The final step is to create your reducer functions, also referred to as "actions". Again, unlike Flux and Redux, actions and reducers are one and the same in Cruiser. Actions take the current model "state" as the first argument and return the new state. Actions can have additional arguments that can be used to inform how the new state will be created.
Once you have an action you can update your store's current state by calling the .dispatch()
method. This method takes the action function as the first argument. If your action has additional arguments, then these can be passed to the .dispatch()
method as well, following the action.
import { createStore } from "cruiser";
type TodosModel = {
todos: string[];
};
const store = cruiser.createStore({
todos: [],
});
const unsubscribe = store.subscribe((state) => {
console.log("State Change", state);
});
function addTodo(state: TodosModel, todo: string): TodosModel {
return { todos: state.todos.concat(todo) };
}
function removeTodo(state: TodosModel, todo: string): TodosModel {
return { todos: state.todos.filter(t => t !== todo) };
}
store.dispatch(addTodo, "mow the lawn");
store.dispatch(addTodo, "walk the dog");
store.dispatch(removeTodo, "mow the lawn");
unsubscribe();
Buffering
When creating a store, you can optionally define a buffer interval that will enable "debouncing" of actions when greater than 0
. What this means is that there is a window of time (the "buffer interval") when actions will be held in a queue until no more actions are dispatched, at which point it will apply all of the buffered actions and the subscribers of the store will be informed of the change only once.
Note: By default, the store has a buffer interval of 25ms
. If you wish to have immediate updates, you'll need to manually specify a buffer interval of 0
.
const noBufferStore = createStore(..., { bufferInterval: 0 });
const bufferedStore = createStore(..., { bufferInterval: 50 });