Skip to content
This repository has been archived by the owner on Feb 16, 2022. It is now read-only.

Latest commit

 

History

History
368 lines (269 loc) · 10.2 KB

api.md

File metadata and controls

368 lines (269 loc) · 10.2 KB

Redux Detector API

The list of all available functions from the redux-detector package.

Redux related API

createDetectorEnhancer(detector: ActionsDetector, listener?: DetectorListener): StoreEnhancer

Creates new StoreDetectableEnhancer that can be passed as a second or third parameter of the createStore function from redux. This enhancer hooks into store's dispatch method and adds replaceDetector method. You can pass second argument which is a listener that will be called with dispatch result (value returned by calling dispatch) or dispatch error.

Example:
import { createStore } from "redux";
import { createDetectorEnhancer } from "redux-detector";
import { rootReducer } from "./store/rootReducer";
import { rootDetector } from "./store/rootDetector";

const store = createStore(rootReducer, createDetectorEnhancer(rootDetector));

createDetectorListener(onNext?: Function, onError?: Function): DetectorListener

Creates new DetectorListener that can be passed as a second argument of the createDetectorEnhancer function. A listener allows to handle all dispatch results and errors done by redux-detector. Functions onNext and onError receives three arguments: result/error, action, and api which is an object: { dispatch, getState } - same as middleware API.

Example:
import { createStore } from "redux";
import { createDetectorEnhancer, createDetectorListener } from "redux-detector";
import { rootReducer } from "./store/rootReducer";
import { rootDetector } from "./store/rootDetector";
import { openSnackbar } from "./store/snackbarAction";

const detectorListener = createDetectorListener(
  (result, action, { dispatch }) => {
    if (result instanceof Promise) {
      result
        .then(asyncResult => console.log({ asyncResult, action }))
        .catch(asyncError => {
          console.error({ asyncError, action });
          dispatch(openSnackbar("Unknown error occurred"));
        });
    } else {
      console.log({ result, action });
    }
  },
  (error, action, { dispatch }) => {
    console.error({ error, action });
    dispatch(openSnackbar("Unknown error occurred"));
  }
);

const store = createStore(
  rootReducer,
  createDetectorEnhancer(rootDetector, detectorListener)
);

composeDetectorListeners(...listeners: DetectorListener[]): DetectorListener

Composes many DetectorListeners into one DetectorListener. Listeners will be called in the same order as passed to this function.

Example:
import { createStore } from "redux";
import {
  createDetectorEnhancer,
  createDetectorListener,
  composeDetectorListeners
} from "redux-detector";
import { rootReducer } from "./store/rootReducer";
import { rootDetector } from "./store/rootDetector";
import { openSnackbar } from "./store/snackbarAction";

const detectorConsoleListener = createDetectorListener(
  (result, action) => {
    if (result instanceof Promise) {
      result
        .then(asyncResult => console.log({ asyncResult, action }))
        .catch(asyncError => console.error({ asyncError, action }));
    } else {
      console.log({ result, action });
    }
  },
  (error, action) => {
    console.error({ error, action });
  }
);

const detectorSnackbarListener = createDetectorListener(
  (result, action, { dispatch }) => {
    if (result instanceof Promise) {
      result.catch(() => dispatch(openSnackbar("Unknown error occurred")));
    }
  },
  (error, action, { dispatch }) => {
    dispatch(openSnackbar("Unknown error occurred"));
  }
);

const detectorListener = composeDetectorListeners(
  detectorConsoleListener,
  detectorSnackbarListener
);

const store = createStore(
  rootReducer,
  createDetectorEnhancer(rootDetector, detectorListener)
);

store.replaceDetector(detector: ActionsDetector): void

Features like hot-reloading or code splitting requires hooks to update rootDetector. You can do it via replaceDetector method available on an enhanced store.

Hot reloading example:
if (module.hot) {
  module.hot.accept("./store/rootReducer", async () => {
    const {
      rootReducer: nextRootReducer
    } = await import("./store/rootReducer");
    store.replaceReducer(nextRootReducer);
  });
  module.hot.accept("./store/rootDetector", async () => {
    const {
      rootDetector: nextRootDetector
    } = await import("./store/rootDetector");
    store.replaceDetector(nextRootDetector);
  });
}

Standard library API

These functions are building blocks for creating more complex detectors. There is no hidden magic in these functions - they are simple and pure.

composeDetectors(...detectors: ActionsDetector[]): ActionsDetector

Composes many actions detectors into one actions detector. If some of detectors returns an action or a list of actions, they are merged into one list of actions.

Example:
import { composeDetectors } from "redux-detector";
import { userDetector } from "./user/userDetector";
import { companyDetector } from "./company/companyDetector";

export const rootDetector = composeDetectors(userDetector, companyDetector);

combineDetectors(map: ActionsDetectorsMap): ActionsDetector

Combines many actions detectors into one actions detector. The logic behind is the same as in the combineReducers function.

Example:
import { combineDetectors } from "redux-detector";
import { blockUser } from "./userActions";

export const blockUserOnLoginAttemptsExceededDetector = combineDetectors({
  login: combineDetectors({
    attempts: (prevAttempts, nextAttempts) =>
      prevAttempts < 3 && nextAttempts >= 3 ? blockUser() : undefined
  })
});

composeAnd(...detectors: ConditionDetector[]): ConditionDetector

Composes many condition detectors into one condition detector using and operation on detectors output.

Example:
import { composeAnd, isTruthy } from "redux-detector";
import { exceededLoginLimit } from "./userDetector";
import { isOnAdminZone } from "../security/securitySelector";

export const exceededLimitOnAdminZone = composeAnd(
  isTruthy(isOnAdminZone),
  exceededLoginLimit
);

composeOr(...detectors: ConditionDetector[]): ConditionDetector

Composes many condition detectors into one condition detector using or operation on detectors output.

Example:
import { composeOr } from "redux-detector";
import { exceededLoginLimit, exceededResetPasswordLimit } from "./userDetector";

export const exceededLoginOrResetPasswordLimit = composeOr(
  exceededLoginLimit,
  exceededResetPasswordLimit
);

composeIf(condition: ConditionDetector, actions: ActionsDetector): ActionsDetector

Creates actions detector that runs inner actions detector only if condition detector output is truthy.

Example:
import { composeIf } from "redux-detector";
import { exceededLoginLimit } from "./userDetector";
import { blockUser } from "./userActions";

const blockUserOnExceededLimitDetector = composeIf(exceededLoginLimit, () =>
  blockUser()
);

mapDetector(selector: Selector<A, B>, detector: Detector<B, C>): Detector<A, C>

Maps state passed to a detector by selector - works perfectly with reselect library.

Example:
import { mapDetector, changedToTruthy } from "redux-detector";
import { getLoginAttempts } from "./userSelector";

export const exceededLoginLimitDetector = mapDetector(
  getLoginAttempts,
  changedToTruthy(attempts => attempts > 3)
);

mapPrevState(...selectors: Selector[]): Detector

Maps previous state passed to a detector by selectors - works perfectly with reselect library. It's a shortcut for mapDetector(createSelector(...selectors), (prevState) => prevState).

mapNextState(...selectors: Selector[]): Detector

Analogous to the mapPrevState.

changed(selector: Selector<A, B>): ConditionDetector<B>

Creates condition detector that checks if next state is not equal previous state (uses === operator)

Example:
import { composeIf, changed } from "redux-detector";
import { getUserId } from "./userSelector";
import { fetchUser } from "./userAction";

export const fetchUserDetector = composeIf(changed(getUserId), () =>
  fetchUser()
);

changedAndTruthy(selector: Selector<A, B>): ConditionDetector<A>

Same as changed with additional requirement - next state has to be truthy.

changedAndFalsy(selector: Selector<A, B>): ConditionDetector<A>

Same as changed with additional requirement - next state has to be falsy.

changedToTruthy(selector: Selector<A, B>): ConditionDetector<A>

Same as changed with additional requirement - previous state has to be truthy.

changedToFalsy(selector: Selector<A, B>): ConditionDetector<A>

Same as changed with additional requirement - previous state has to be falsy.

isEqual(selector: Selector<A, B>, expectedNextState: B): ConditionDetector<A>

Creates condition detector that checks if next state is equal expected next state.

Example:
import { composeIf, isEqual, changedAndTruthy } from "redux-detector";
import { getUserRole, getUserId } from "./userSelector";
import { fetchAdminPermissions } from "./userAction";

export const fetchUserDetector = composeIf(
  composeAnd(isEqual(getUserRole, "ROLE_ADMIN"), changedAndTruthy(getUserId)),
  () => fetchAdminPermissions()
);

isTruthy(selector: Selector<A, B>): ConditionDetector<A>

Creates condition detector that checks if next state is truthy.

Example:
import { composeIf, isTruthy, changedAndTruthy } from "redux-detector";
import { isAdmin, getUserId } from "./userSelector";
import { fetchAdminPermissions } from "./userAction";

export const fetchUserDetector = composeIf(
  composeAnd(isTruthy(isAdmin), changedAndTruthy(getUserId)),
  () => fetchAdminPermissions()
);

isFalsy(selector: Selector<A, B>): ConditionSelector<A>

Creates condition detector that checks if next state is falsy.