conduxion
TypeScript icon, indicating that this package has built-in type declarations

1.0.7 • Public • Published

Conduxion

Consequential redux actions

A small API extracting the goodness of https://github.com/edumentab/talks-redux-patterns by @krawaller.

import {actionCreatorFactory} from 'conduxion'
import produce from 'immer'
 
import {AppActionMould} from '../../../state.types';
 
import {STATE_KEY} from '../authentication.state';
import {setAppError} from '../../ui';
 
type SetIsAuthenticatedPayload = {
    isAuthenticated: boolean
}
 
export type SetIsAuthenticatedAction = AppActionMould<'SET_IS_AUTHENTICATED', SetIsAuthenticatedPayload>
 
export const [setIsAuthenticated, isSetIsAuthenticated] = actionCreatorFactory<SetIsAuthenticatedAction>({
     type: 'SET_IS_AUTHENTICATED',
     reducer(state, payload) {
         const {isAuthenticated} = payload;
    
         return produce(state, draft => {
             draft[STATE_KEY].isAuthenticated = isAuthenticated;
         })
     },
     consequence({action, dispatch}) {
         const {isAuthenticated} = action.payload;
         
         if (!isAuthenticated) {
             dispatch(setAppError('Not authenticated'));
             return;
         }
     }
});

Table of Contents

Philosophy

An alternative approach to redux where we put actions first, and view state as a consequence of them.

Instead of creating singleton reducers responsible for a single state slice, we create reducers for each individual action that can act across the entire state.

Should an action have consequences, they are declared on the action itself (as an event-emitter).

Installation

$ npm i -S conduxion

Please note that redux is a peer-dependency.

Usage

Creating a store

Using pre-fab factories.

import {Store} from 'redux';
import {makeStore, RootReducer, ConduxionAction} from 'conduxion';
 
type AppState = {
    // whatever you want
    [key: string]: any
}
type AppDependencies = {}
type AppAction = ConduxionAction<AppState, AppDependencies>
const INITIAL_APP_STATE: AppState = {}
 
const rootReducer: RootReducer<AppState, AppAction, AppDependencies> = (state = INITIAL_APP_STATE, action) => action.reducer
    ? action.reducer(state, action.payload)
    : state;
 
const store: Store<AppState, AppAction> = makeStore<AppState, AppAction, AppDependencies>(
    rootReducer,
    INITIAL_APP_STATE
);
 
export default store;

For your convenience, we also expose a dev version that automatically connects to Redux DevTools

import {Store} from 'redux';
import {createActionLogMiddleware, makeDevStore, RootReducer, ConduxionAction} from 'conduxion';
 
type AppState = {
    // whatever you want
    [key: string]: any
}
type AppDependencies = {}
type AppAction = ConduxionAction<AppState, AppDependencies>
 
const INITIAL_APP_STATE: AppState = {}
 
const rootReducer: RootReducer<AppState, AppAction, AppDependencies> = (state = INITIAL_APP_STATE, action) => action.reducer
    ? action.reducer(state, action.payload)
    : state;
 
const actionLog: any[] = [];
 
const store: Store<AppState, AppAction> = makeDevStore<AppState, AppAction, AppDependencies>(
    rootReducer,
    INITIAL_APP_STATE,
    {
        // createActionLogMiddleware mutates actionLog with each action fired
        additionalMiddleware: [createActionLogMiddleware(actionLog)]
    }
);
 
export default store;

Creating an action

Using the actionCreatorFactory() factory function.

import {actionCreatorFactory, ConduxionActionMould} from 'conduxion'
 
import {AppState, AppDependencies} from '../somewhere'
import {setAppError} from '../somewhere-else'
 
type AppActionMould<T extends string, P> = ConduxionActionMould<
    T,
    P,
    AppState,
    AppDependencies
>
 
type SetIsAuthenticatedPayload = {
    isAuthenticated: boolean
}
 
export type SetIsAuthenticatedAction = AppActionMould<'SET_IS_AUTHENTICATED', SetIsAuthenticatedPayload>
 
export const [setIsAuthenticated, isSetIsAuthenticated] = actionCreatorFactory<SetIsAuthenticatedAction>({
    type: 'SET_IS_AUTHENTICATED',
    // optional
    reducer(state, payload) {
        const {isAuthenticated} = payload;
 
        return {
            ...state,
            isAuthenticated 
        }
    },
    // optional
    consequence({action, dispatch}) {
         const {isAuthenticated} = action.payload;
         
         if (!isAuthenticated) {
             dispatch(setAppError('Not authenticated'));
             return;
         }
    }
});

API

makeStore

Creates a conduxion redux store with consequence middleware applied.

import {Store} from 'redux'
import {ConduxionAction, RootReducer, MakeStoreOptions} from 'conduxion';
 
export function makeStore<State extends object, A extends ConduxionAction<State, Dependencies>, Dependencies extends object>(
    rootReducer: RootReducer<State, A, Dependencies>,
    initialState: State,
    opts: MakeStoreOptions<State, Dependencies> = {}
): Store<State, A>;

makeDevStore

Creates a conduxion redux store with consequence middleware as well as Redux DevTools middleware applied.

import {Store} from 'redux'
import {ConduxionAction, RootReducer, MakeStoreOptions} from 'conduxion';
 
export function makeStore<State extends object, A extends ConduxionAction<State, Dependencies>, Dependencies extends object>(
    rootReducer: RootReducer<State, A, Dependencies>,
    initialState: State,
    opts: MakeStoreOptions<State, Dependencies> = {}
): Store<State, A>;

actionCreatorFactory

Factory function returning an ActionCreator and ActionGuard for a gived redux action.

import {
    Action,
    ActionType,
    ActionReducer,
    ActionState,
    ActionPayload,
    Consequence,
    ActionDeps,
    ConduxionAction,
    ActionCreator,
    ActionGuard
} from 'conduxion'
 
type CreatorBlueprint<A extends Action<string, any, any, any>> = {
    type: ActionType<A>
    reducer: ActionReducer<ActionState<A>, ActionPayload<A>>
    isError?: boolean
    consequence?: Consequence<ActionState<A>, ActionDeps<A>, ActionPayload<A>>
}
 
export default function actionCreatorFactory<A extends ConduxionAction<any, any>>(
    blueprint: CreatorBlueprint<A>
): [ActionCreator<A>, ActionGuard<A>]

Types

There are a lot of types exposed by conduxion, here are a few of them. Please see the root types as well as the core types for a comprehensive list.

RootReducer

Redux root reducer for conduxion.

import {ConduxionAction} from 'conduxion'
 
export type RootReducer<
    State extends object,
    A extends ConduxionAction<State, Dependencies>,
    Dependencies extends object
= (state: State | undefined, action: A) => State;

Action

The base interface for all conduxion actions. Enables use of consequence methods.

import {ActionReducer, Consequence} from 'conduxion';
 
export interface Action<
    Type extends string,
    Payload extends any,
    State extends object,
    Dependencies extends object> {
    type: Type
    error?: boolean
    sender?: string
    reducer?: ActionReducer<State, Payload>
    payload: Payload
    consequence?: Consequence<State, Dependencies, Payload> | Consequence<State, Dependencies, Payload>[]
}

Consequence

An action consequence method.

import {ConsequenceAPI} from 'conduxion'
 
export type Consequence<State extends object, Dependencies extends object, Payload extends any = any> = ((
    apiConsequenceAPI<State, Dependencies, Payload>
) => void) & {
    name: string,
    displayName?: string
}

ConduxionActionMould

Generic representation of a conduxion action.

import {Action} from 'conduxion';
 
export type ConduxionActionMould<
    Type extends string,
    Payload,
    State extends object,
    Dependencies extends object
= Action<Type, Payload, State, Dependencies>

MakeStoreOptions

Additional configuration for makeStore and makeDevStore.

import {Middleware} from 'redux';
import {ConsequenceGetter, Consequence} from 'conduxion';
 
export type MakeStoreOptions<State extends object, Dependencies extends object> = {
    additionalMiddleware?: Middleware[]
    dependencies?: Dependencies
    consequenceGetter?: ConsequenceGetter<State, Dependencies>
    initConsequence?: Consequence<State, Dependencies>
}

Package Sidebar

Install

npm i conduxion

Weekly Downloads

108

Version

1.0.7

License

MIT

Unpacked Size

31.4 kB

Total Files

51

Last publish

Collaborators

  • huboneo