2.2.4 • Published 3 years ago

redux-utility v2.2.4

Weekly downloads
3
License
MIT
Repository
github
Last release
3 years ago

Redux Utility Toolbelt

This a Redux utilities library for alternate approaches to creating all the redux boilerplate. It uses ramda under the hood for most of the utilities.

Install

yarn add redux-utility
# or
npm install redux-utility

Migration from 1.x to 2.x

Since exports were reorganized, all that is needed is to update exports. Change this

import { Reducers } from 'redux-utility'

const { createReducer } = Reducers;

to

import { createReducer } from 'redux-utility'

Usage

The functions in the library are divided into 6 categories:

  • Actions
  • Hooks
  • Reducers
  • Redux
  • Observable
  • Modules

Every function inside each module is exported as a named export. A plan for rxjs like imports is on the way

Actions

The Actions object contains functions for creating action creators.

import { 
    nullaryAction,
    unaryActionCreator,
    nAryActionCreator,
    shape
} from 'redux-utility'

nullaryActionCreator

Creates an action creator with no arguments, given the type

const inc = nullaryActionCreator("INC")
inc() // returns { type: "INC" }

unaryActionCreator

Creates an action creator that receives a single argument, given the type

const withPayload = unaryActionCreator("WITH_PAYLOAD");
withPayload(42) // returns { type: "WITH_PAYLOAD" , payload: 42 }

nAryActionCreator

Creates an action creator given the type and a payload constructor. Payload is constructed by calling the payload function with the given arguments

const withTwoArgs = nAryActionCreator("ADD",(a,b) => ({ a,b }))
withTwoArgs(20,22) // returns { type: "ADD" , payload: { a: 20, b: 22 } }

shape

Shape is a simple utility added for the most common case shape receives the names of the object keys and returns a function that constructs objects based on argument position

const ABShape = shape('a','b')
ABShape(1,2) // returns { a: 1, b: 2 }

const withAB = nAryActionCreator("ADD", ABShape)
withAB(20,22) // returns { type: "ADD" , payload: { a: 20, b: 22 } }

Hooks

import { 
    usePathSelector 
} from 'redux-utility'

usePathSelector

The hook receives a dot separated path and a default value and returns a piece of state

// state = { a : { b: { c: 42 } } }

usePathSelector("a.b.c") // returns 42
usePathSelector("a.b.d") // returns undefined
usePathSelector("a.b.d",50) // returns 50

Reducers

Contains various functions that create reducers through different styles

import { 
    createReducer,
    createPairsReducer, 
    createEventReducer  
} from 'redux-utility'

createReducer

Receives a configuration object and creates a reducer based on the object. Uses the keys as action types and calls the value associated with the key. Also a special 'default' key is reserved for when no action is handled. The default value for the default key is the identity function

const DEC = 'DEC'
const INC = 'INC'

const reducer = createReducer({
    [DEC]: (state,action) => state - 1,
    [INC]: (state,action) => state + 1,
    // default: (state) => state
})

const dec = nullaryActionCreator(DEC)
const inc = nullaryActionCreator(INC)

reducer(5,dec()) // returns 4
reducer(5,inc()) // returns 6
reducer(5, {type: "anything"}) // returns 5

createPairsReducer

Creates a reducer based on an array of (actionType, function) pairs. Converts the array to an object and calls createReducer. Keep in mind that the array is not validated and must conform to a valid object.

const DEC = 'DEC'
const INC = 'INC'

const reducer = createPairsReducer([
    [DEC, (state,action) => state - 1],
    [INC, (state,action) => state + 1],
    // ["default", (state) => state]
])

const dec = nullaryActionCreator(DEC)
const inc = nullaryActionCreator(INC)

reducer(5,dec()) // returns 4
reducer(5,inc()) // returns 6
reducer(5, {type: "anything"}) // returns 5

createEventReducer

Creates a reducer based on a function that receives an object with a similar interface of that of NodeJS Event Emitters, where events are action types. Has a default event for handling the default case.

const DEC = 'DEC'
const INC = 'INC'

const reducer = createEventReducer(reducer =>
    reducer.on(DEC, (state,action) => state - 1)
    reducer.on(INC, (state,action) => state + 1)
    // reducer.on("default", (state) => state)
])

const dec = nullaryActionCreator(DEC)
const inc = nullaryActionCreator(INC)

reducer(5,dec()) // returns 4
reducer(5,inc()) // returns 6
reducer(5, {type: "anything"}) // returns 5

Redux

Contains general redux utilities

import {
    getDevtoolsCompose
} from 'redux-utility'

getDevtoolsCompose

Returns the redux devtools compose if it exist. Otherwise returns redux compose. The function may receive an argument that when false, forces the function to return redux's compose. This could be a function or a boolean value. Common usage:

const shouldUseDevtool = () => process.env.NODE_ENV === "development"
const composeEnhancers = getDevtoolsCompose(shouldUseDevtool)

const store = createStore(
    reducer,
    initialState,
    composeEnhancers(...enhancers)
)

Observable

Contains utilities to use with redux-observable

import {
    fromActions,
    fromActionsEager
} from 'redux-utility'

fromActions

Creates an observable creator function from the given action creators. The observable created emits the actions in the given order by passing the given arguments to the action creators. Useful when a piece of state is needed or the actions have a payload. When the emitted actions have no needed payload, it is commonly better to use the eager version of this utility.

const DEC = 'DEC'
const INC = 'INC'

const reducer = createReducer({
    [DEC]: (state,action) => state - action.payload,
    [INC]: (state,action) => state + action.payload,
})

const inc = unaryActionCreator("INC")
const dec = unaryActionCreator("DEC")

const obs = fromActions( inc, dec )(5)
// --- inc(5) --- dec(5) |--->

// Common usage
const epic = actions$ => actions$.pipe(
    ofType("START"),
    map(() => 5)
    mergeMap(
        fromActions(
            inc,
            dec
        ) // returns the same as fromActions( inc, dec )(5)
    )
)

fromActionsEager

Eager version of fromActions. Creates an action observable from the given action creators by calling them with no arguments. Similar to calling rxjs Observable.from with an array of actions.

const DEC = 'DEC'
const INC = 'INC'

const reducer = createReducer({
    [DEC]: (state,action) => state - 1,
    [INC]: (state,action) => state + 1,
})

const inc = nullaryActionCreator("INC")
const dec = nullaryActionCreator("DEC")

const obs = fromActionsEager( inc, dec )
// --- inc() --- dec() |--->

// Common usage
const epic = actions$ => actions$.pipe(
    ofType("START"),
    mergeMap(() =>
        fromActionsEager(
            inc,
            dec
        ) // returns the same as fromActionsEager( inc, dec )
    )
)

Modules

Contains common use cases ready to be used. Currently contains:

  • createAsyncState

createAsyncState

Common pattern for async state. creates a fetch, success, and error action with their respective constants, reducer function, reducer config, reducer register function, reducer pairs config and reducer initial state. The usage is as follows:

import { createAsyncState } from 'redux-utility';

// Basic usage
// receives a key for namespacing the constants. The constants will be prefixed with the key
const reducer = createAsyncState("user");

// optionally, if you want the state to be nested inside the key provided
const nested = createAsyncState("user",{ nested: true });

export default reducer;

export const {
    fetch: FETCH_USER,
    success: USER_SUCCESS,
    error: USER_ERROR
} = reducer.constants;

export const {
    fetch: fetchUser,
    success: userSuccess,
    error: userError
} = reducer.actions;

// more options added for flexibility

createReducer(reducer.config);
createPairsReducer(reducer.pairs);
createEventReducer(reducer.register);

// mixing with other pieces of state

const OTHER = "OTHER";
createReducer({
    ...reducer.config,
    [OTHER]: (state) => ({ ...state, other: true }),
})
createPairsReducer([
    ...reducer.pairs,
    [OTHER, (state) => ({ ...state, other: true })]
])
createEventReducer((handler) => {
    reducer.register(handler);
    handler.on(OTHER,(state) => ({ ...state, other: true }))
})

// extension of reducer

const async = createAsyncState("auth")
const extendedReducer = async.extend({
    success: (state,action) => {
        return {
            ...state,
            authenticated: true
        }
    }
})

// or the long way around
const extended = createReducer({
    [aync.constants.success]: async.extend.success((state,action) => {
        return {
            ...state,
            authenticated: true
        }
    }),
})

// this are the possible states

const initialState = reducer.initialState

// {
//     loading: false,
//     data: undefined,
//     error: undefined
// }

dispatch(fetchUser())

// {
//     loading: true,
//     data: undefined,
//     error: undefined
// }

dispatch(userSuccess({ name: "User" }))

// {
//     loading: false,
//     data: { name: "user" },
//     error: undefined
// }

dispatch(userError("Something went wrong"))

// {
//     loading: false,
//     data: undefined,
//     error: "Something went wrong"
// }
2.2.3

3 years ago

2.2.2

3 years ago

2.2.4

3 years ago

2.2.1

4 years ago

2.2.0

4 years ago

2.1.0

4 years ago

2.0.0

4 years ago

1.0.3

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago