redux-app
Type-safe, DRY and OO redux. Implemented with typescript.
Installation
npm install --save redux-app
Short Example
@component
class App {
counter = new Counter();
}
@component
class Counter {
value = 0;
increment() {
this.value = value + 1;
}
}
const app = new ReduxApp(new App(), devToolsEnhancer(undefined));
console.log(app.root.counter.value);
console.log(app.store.getState());
app.root.counter.increment();
console.log(app.root.counter.value);
console.log(app.store.getState());
Important Notice
You should not mutate the object properties but rather assign them with new values.
That's why we write this.value = value + 1
and not this.value++
.
More Examples
More examples can be found here redux-app-examples.
How it works
For each component
decorated class the library generates an underlying Component
object that holds the same properties and method.
The new Component object has it's prototype patched and all of it's methods replaced with dispatch() calls.
The generated Component also has a hidden 'REDUCER' property which is later on used by redux store. The 'REDUCER' property itself is
generated from the original object methods, replacing all 'this' values with the current state from the store on each call (using
Object.assign and Function.prototype.call).
Documentation
Async Actions
Async actions and side effects are handled in redux-app by using either the sequence
decorator or the noDispatch
.
Both decorators does exactly the same and are actually aliases of the same underlying function. What they do is
to tell redux-app that the decorated method is a plain old javascript method and that it should not be patched (about
the patch process see How it works). So, to conclude, what these decorators actually do is to tell
redux-app to do nothing special with the method.
Usage:
working example can be found on the redux-app-examples page
@component
class MyComponent {
public setStatus(newStatus: string) {
this.status = newStatus;
}
@sequence
public async fetchImage() {
this.setStatus('Fetching...');
var response = await fetch('fetch something from somewhere');
var responseBody = await response.json();
this.setStatus('Adding unnecessary delay...');
setTimeout(() => {
this.setStatus('I am done.');
}, 2000);
}
@noDispatch
public doNothing() {
console.log('I am a plain old method. Nothing special here.');
}
}
withId
The rule of the withId
decorator is double. From one hand, it enables the co-existence of two (or more) instances of the same component,
each with it's own separate state. From the other hand, it is used to keep two separate components in sync. The 'id' argument of the decorator
can be anything (string, number, object, etc.).
Example:
working example can be found on the redux-app-examples page
@component
export class App {
@withId('SyncMe')
public counter1 = new CounterComponent();
@withId('SyncMe')
public counter2 = new CounterComponent();
@withId(123)
public counter3 = new CounterComponent();
@withId()
public counter4 = new CounterComponent();
}
Options
Component Options
You can supply the following options to the component
decorator.
class SchemaOptions {
public actionNamespace?: boolean;
public uppercaseActions?: boolean;
public updateState?: boolean;
}
Usage:
@component({ uppercaseActions: false })
class Counter {
value = 0;
increment() {
this.value = value + 1;
}
}
Global Options
Available global options:
class GlobalOptions {
logLevel: LogLevel;
schema: SchemaOptions;
}
enum LogLevel {
None = 0,
Verbose = 1,
Debug = 2,
Silent = 10
}
Usage:
ReduxApp.options.logLevel = LogLevel.Debug;