npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

conduxion

v1.0.7

Published

Consequential Redux Actions

Downloads

94

Readme

Conduxion

Con~~sequential~~ ~~re~~dux ~~act~~ion~~s~~

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> = ((
    api: ConsequenceAPI<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>
}