Redux ported to java/android
I've seen a few of these floating around, but this one has some specific benefits over other implementations.
- Any object can be used as an action or state.
- Built-in functions to help compose reducers
- Middleware that's actually implemented like you'd expect.
- A port of the thunk middleware.
- A fully-fleshed android sample.
Create a store.
ObservableStore<State> store = new Observable<>(initialState, reducer, middleware...);
Get the current state.
State state = store.state();
Listen to state changes.
store.addListener(new Listener<State>() {
@Override
public void onNewState(State state) {
...
}
});
Or with rxjava (you must explicitly declare it as a dependency).
store.observable().subscribe(state -> { ... });
Dispatch actions.
store.dispatch(new MyAction());
I suggest you use the StateLoader
as it will tie your state updates with the activity/fragment lifecycle and ensure callbacks happen on the main thread.
public class MyActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<State> {
ObservableStore<State> store = ...;
@Override
protected void onCreate(Bundle savedInstanceState) {
getSupportLoaderManager().initLoader(0, null, this);
}
@Override
public Loader<State> onCreateLoader(int id, Bundle args) {
return new StateLoader<>(this, store);
}
@Override
public void onLoadFinished(Loader<State> loader, State data) {
...;
}
@Override
public void onLoaderReset(Loader<TodoList> loader) {
}
}
It's common you'd want to switch on actions values or class type. Reducers.matchValue()
and Reducers.matchClass()
makes this easy.
Reducer<String, State> reducer = Reducers.matchValue()
.when("action1", new Action1Reducer())
.when("action2", new Action2Reducer());
Reducer<Object, State> reducer = Reducers.matchClass()
.when(Action1.class, new Action1Reducer())
.when(Action2.class, new Action2Reducer());
There is also Reducers.match()
which takes a predicate for more complicated matching setups.
Reducer<Object, State> reducer = Reducers.match()
.when(Predicates.is("action1"), new Action1Reducer())
.when(Predicates.instanceOf(Action2.class), new Action2Reducer());
You can also run a sequence of reducers with Reducers.all(reducer1, reducer2, ...)
or run reducers until one changes the state with Reducers.first(reducer1, reducer2, ...)
.
Allows you to dispatch async functions as actions.
ObservableStore<State> store = new ObservableStore<>(initialState, reducer, new ThunkMiddleware<State>());
store.dispatch(new Thunk<State>() {
@Override
public void run(Store<State> store) {
store.dispatch(new StartLoading());
someAsyncCall(new Runnable() {
@Override
public void run() {
store.dispatch(new StopLoading());
}
}
}
});
Alternatively, you can use the ObservableMiddleware
to dispatch a stream of actions.
ObservableStore<State> store = new ObservableStore<>(initialState, reducer, new ObservableMiddleware<State>());
store.dispatch(callThatReturnsObservable()
.map(result -> new StopLoading())
.startWith(Observable.just(new StartLoading())));