Quick simple guide on making Redux-Observable works with TypeScript

in #javascript6 years ago (edited)

This is a guide I written on stack overflow, which I plan to rewrite it into a simple post.

You can checkout React Redux Typescript Guide for the details in here for all the standard way of doing when using React, Redux and Redux-Observable.

Introduction

1_fdzQpORTUt1yCFFOvD_whg.png

Image Credit

This guide I would like to talk a bit about using Redux-Observable alongside with TypeScript and make the types work out of the box.

I will be using typesafe-actions library to achieve the types. (GitHub for typesafe-actions)

Actions

Instead of declaring interface (types) expressively, (something like below)

export interface LoginSuccessAction extends Action {
  type: LoginActionTypes.LOGIN_SUCCESS_ACTION;
  payload: {
    loginToken: string;
  };
}

export function loginSuccess(loginToken: string): LoginSuccessAction {
  return {
    type: LoginActionTypes.LOGIN_SUCCESS_ACTION,
    payload: { loginToken },
  };
}

use typesafe-actions library without declaring any interface.

actions/login/LoginActions.ts

import {action} from "typesafe-actions"

export function loginSuccess(loginToken: string) {
  return action(LoginActionTypes.LOGIN_SUCCESS_ACTION, { loginToken });
}

Then, all the login actions are being exported out in the model file.

actions/login/LoginActionsModel.ts

import * LoginActions from "./LoginActions";
import { ActionCreator } from "typesafe-actions";

export type LoginActionCreator = ActionCreator<typeof LoginActions>

Then export out all the actions as AllActions.

actions/index.ts

import { LoginActionCreator } from "./login/LoginActionModel";

export default type AllActions = LoginActionCreator

Epics

In the epics, import following library.

import { Epic } from "redux-observable";
import { isOfType } from "typesafe-actions";
import { filter } from "rxjs/operators";

Then simply use Epic to declare your types.

export const loginUserEpic: Epic<AllActions> = (action$) =>
  action$.pipe(
    filter(isOfType((LoginActionTypes.LOGIN_ACTION))),
    switchMap((action: LoginAction) =>
      ajax({
        url,
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: { email: action.payload.username, password: action.payload.password },
      }).pipe(
        map((response: AjaxResponse) => loginSuccess(response.response.token)),
        catchError((error: Error) => of(loginFailed(error))),
      ),
    ),
  );

Where the Epics is from redux-observable library, AllActions are the actions that is input and output of the epics.

The types is as follows:

Epic<InputActions, OutputActions, Store>
Epic<Actions(Input&Output)>

In case you want to use store from redux, you need a RootState (from root reducer)

export const someEpic: Epic<AllActions, AllActions, RootState> = (action$, store$) 
=> action$.pipe(
  filter(isOfType(SOMETYPE)),
  mergeMap(action => {
    const a = store$.value.a;
    return someActions(a, action.payload.b);
  })
Sort:  

You’ve been upvoted by TeamMalaysia Community :-

To support the growth of TeamMalaysia Follow our upvotes by using steemauto.com and follow trail of @myach

Vote TeamMalaysia witness bitrocker2020 using this link vote for witness

你今天过的开心吗?来 @steemgg 玩游戏吧,决战到天亮如果不想再收到我的留言,请回复“取消”。

Hi, @superoo7!

You just got a 0.91% upvote from SteemPlus!
To get higher upvotes, earn more SteemPlus Points (SPP). On your Steemit wallet, check your SPP balance and click on "How to earn SPP?" to find out all the ways to earn.
If you're not using SteemPlus yet, please check our last posts in here to see the many ways in which SteemPlus can improve your Steem experience on Steemit and Busy.