-
Notifications
You must be signed in to change notification settings - Fork 7
Async operations and side effects
We use reducers to modify our state and reducers must be pure functions which are free of side-effects. Webapplications are however subject to quite some side effects (http requests, user inputs, etc.). We therefore need a way to handle asynchronous operations and side effects in a clean way.
In Redux-land, several approaches exist to handle async operations (i.e. ActionCreators) that often require custom libraries/middlewares to be handled properly (examples of such libraries are redux-thunk or redux-saga). For Angular applications, the ngrx effects library introduces the concept of Effects
to handle asynchronous operations.
Since reactive-state builds on top RxJS - which is a library that makes it easy to combine and chain asynchronous operations - handling side effects is really easy with reactive-state, and no special middlewares or additional libraries are required.
We just use the multitude of available operators in RxJS to map our Actions (which are Observables/Subjects) to perform any asynchronous operations, like HTTP requests. The resulting observable can be used as an Action and directly be used in the .addReducer()
function of a Store:
interface AppState {
myIpAddress: string;
}
const store: Store<AppState> = Store.create({ myIpAddress: "" });
const getIpAddress = new Subject<void>();
const getIpAddressEffect = getIpAddress.pipe(
switchMap(() => fetch("http://ip.jsontest.com")),
switchMap(response => response.json()),
map(dto => dto.ip)
);
const ipAddressReducer: Reducer<AppState, string> =
(state, ip) => ({ ...state, myIpAddress: ip });
store.addReducer(getIpAddressEffect, ipAddressReducer);
store.watch().pipe(
filter(state => state.myIpAddress.length > 0)
).subscribe(state => console.log("My IP address is: ", state.myIpAddress));
// dispatch the action, only this will trigger the HTTP request!
getIpAddress.next();
// output: "My IP address is: 1.2.3.4"
In the above example, we passed an Observable to the .addReducer()
function. This is perfectly fine and encouraged in reactive-state. The Observable that we wired, is dependent on and action that we can trigger whenever we want, especially in an async way. When adding an Observable via .addReducer()
, you can manually specify a string constant to name this "pseudo-action". Naming is optional, but useful for logging and debugging, especially when using the dev tools:
store.addReducer(getIpAddressEffect, ipAddressReducer, "IP_ADDRESS_RECEIVED");