Skip to content

rafde/react-hook-use-cta

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

react-hook-use-cta: useCTA (use Call To Action)

A somewhat flexible react hook alternative to React.useReducer. Written in Typescript.

Demo Playground

Table of Contents

Table of Contents: Parameter
Table of Contents: Return Type
Table of Contents: Typescript exports

Installation

react-hook-use-cta fast-equals

NPM

npm i react-hook-use-cta fast-equals

export { useCTA }

export function useCTA<
Initial extends CTAInitial,
Actions extends UseCTAParameterActionsRecordProp<Initial> | undefined
>(
useCTAParameter: UseCTAParameter<Initial, Actions>,
): UseCTAReturnType<Initial, Actions> {

Usage

import { useEffect, } from "react";
import { useCTA, } from 'react-hook-use-cta'

function View() {
	const [
		state,
		dispatch,
	] = useCTA({
		initial: {
			search: 'initial',
			isFuzzy: false, 
			count: 0,
		}
	});

	useEffect(
		() => dispatch.cta.update('search', 'update'),
		[]
	);

	/* Renders `update` */
	return <>{state.search}</>
}
Example using all useCTA parameters
import { useEffect, } from "react";
import { useCTA, } from 'react-hook-use-cta'

function View(props: { initial: { search: string, isFuzzy: boolean, count: 0 } }) {
	const [
		state,
		dispatch,
	] = useCTA({
		initial: props.initial,
		onInit(initial) {
			return {
				...initial,
				search: 'onInit',
			}
		},
		actions: {
			// START: augment predefined actions
			replace(ctaParam, payload) {
				return payload;
			},
			replaceInitial(ctaParam, payload) {
				return payload;
			},
			reset(ctaParam, payload) {
				return payload;
			},
			update(ctaParam, payload) {
				return payload;
			},
			// END: augment predefined actions

			// START: Custom actions
			toggleIsFuzzy(ctaParam, isFuzzy?: boolean) {
				if (typeof isFuzzy === 'undefined') {
					return {
						isFuzzy: !ctaParam.previous.isFuzzy,
					}
				}

				return {
					isFuzzy
				}
			},
			addToCount(ctaParam, value: number) {
				return {
					count: ctaParam.previous.count + value,
				}
			},
			incrementCount(ctaParam) {
				return {
					count: ctaParam.previous.count + 1,
				}
			},
			// END: Custom actions
		}
	});

	useEffect(
		() => dispatch.cta.update('search', 'update'),
		[]
	);

	return <>
		<div>{state.search}</div>
		<div>{dispatch.state.initial.search}</div>
		<div>{dispatch.state.changes?.search}</div>
		<div>{dispatch.state.previous.search}</div>
	</>
}

Parameter

Required

Key/value object of type UseCTAParameter

export type UseCTAParameter<
Initial extends CTAInitial,
Actions extends UseCTAParameterActionsRecordProp<Initial> | undefined
> = {
actions?: Actions,
initial: Initial,
onInit?: ( ( initial: Initial ) => Initial )
}

Typescript Definition:


Parameter: initial

Required

Similar to React.useReducer initialArg parameter, but it only takes key/value object that defines the shape of your state.

Values can be anything that strictDeepEqual from fast-equals supports.

Typescript Definition:


Parameter: onInit

Optional

onInit?: ( ( initial: Initial ) => Initial )

Similar to React.useReducer init parameter. Called on first time render. A function that is called to replace initial value.

Typescript Definition:


Parameter: actions

Optional

Read once on first time render. Key/value object to define the types of actions to implement.

The following results will not trigger re-render for all actions:

Predefine actions

There are predefined actions that can be augmented with the following signatures:

export type UseCTAParameterActionsPredefinedRecord<Initial extends CTAInitial,> = {
replace?: ( ctaState: UseCTAReturnTypeDispatchState<Initial>, payload: Initial ) => Initial | undefined
replaceInitial?: ( ctaState: UseCTAReturnTypeDispatchState<Initial>, payload: Initial ) => Initial | undefined
reset?: ( ctaState: UseCTAReturnTypeDispatchState<Initial>, payload?: Initial ) => Initial | undefined
update?: ( ctaState: UseCTAReturnTypeDispatchState<Initial>, payload: Partial<Initial> ) => Partial<Initial> | undefined
};

Augmenting these actions will affect custom actions.

Typescript Definition:

Predefined calls to action:


Parameter: actions?.['customAction']

You can define your own custom actions to handle payloads to your specifications.

Typescript signature:

export type UseCTAParameterActionsCustomRecord<Initial extends CTAInitial,> = {
[customAction: string | number]: (
(
ctaParam: CustomCTAParam<Initial>,
// Needs to be `any` in order to take any type.
payload?: any // eslint-disable-line
) => CustomCTAReturnType<Initial>
)
};

Typescript Definitions:

Call to action:

Example for Parameter: actions?.['customAction']
import { useCTA } from 'react-hook-use-cta'

function View(props: {initial: {search: string, isFuzzy: boolean, count: 0}}) {
	const [
		state,
		dispatch,
	] = useCTA({
		initial: props.initial,
		onInit(initial) {
			return {
				...initial,
				search: 'onInit',
			}
		},
		actions: {
			// START: Custom actions
			toggleIsFuzzy(ctaParam, payload?: boolean) {
				if (typeof payload === 'undefined') {
					return {
						isFuzzy: !ctaParam.previous.isFuzzy,
					}
				}
				
				return {
					isFuzzy: payload,
				}
			},
			addToCount(ctaParam, payload: number) {
				const count = ctaParam.previous.count + payload;
				if (isNaN(count) || count < 0) {
					return;
				}
				return {
					count,
				}
			},
			incrementCount(ctaParam) {
				return {
					count: ctaParam.previous.count + 1,
				}
			},
			// END: Custom actions
		}
	});

	useEffect(
		() => dispatch.cta.addToCount(4),
		[]
	);

	return <>{state.count}</>
}

actions?.['customAction'] 1st Parameter: CustomCTAParam

Custom actions' first parameter:

export type CustomCTAParam<Initial extends CTAInitial,> = UseCTAReturnTypeDispatchState<Initial> & {
replaceAction( result: Initial, options?: ActionTypeConstructParam<Initial>['options'] ): ReplaceActionType<Initial>
replaceInitialAction( result: Initial, options?: ActionTypeConstructParam<Initial>['options'] ): ReplaceInitialActionType<Initial>
resetAction( result: Initial, options?: ActionTypeConstructParam<Initial>['options'] ): ResetActionType<Initial>
updateAction( result: Partial<Initial>, options?: ActionTypeConstructParam<Initial>['options'] ): UpdateActionType<Initial>
};

extends UseCTAReturnTypeDispatchState with 4 additional functions that affect the behavior of the action.

replaceAction( result: Initial, options?: ActionTypeConstructParam<Initial>['options'] ): ReplaceActionType<Initial>
replaceInitialAction( result: Initial, options?: ActionTypeConstructParam<Initial>['options'] ): ReplaceInitialActionType<Initial>
resetAction( result: Initial, options?: ActionTypeConstructParam<Initial>['options'] ): ResetActionType<Initial>
updateAction( result: Partial<Initial>, options?: ActionTypeConstructParam<Initial>['options'] ): UpdateActionType<Initial>

Accepts result and options

options?: { useCustom: boolean }

If predefined actions were augmented, {useCustom: false} will bypass them and use default predefined behavior.


actions?.['customAction'] 1st Parameter: CustomCTAParam.replaceAction

replaceAction( result: Initial, options?: ActionTypeConstructParam<Initial>['options'] ): ReplaceActionType<Initial>

Returns instance of ReplaceActionType

export class ReplaceActionType<Initial extends CTAInitial,> extends ActionType<Initial> {

Returning ReplaceActionType will produce the same outcome as dispatch.cta.replace

Example for CustomCTAParam.replaceAction
const [state, dispatch] = useCTA({
	///...parameter
	actions: {
		specialReplace(ctaParam, payload?: boolean) {
			if (typeof payload === 'undefined') {
				// replace `state` with this new state
				return ctaParam.replaceAction({
					...ctaParam.previous,
					isFuzzy: true,
				})
			}
			
			// update state
			return {
				isFuzzy: payload,
			}
		},
	},
});

actions?.['customAction'] 1st Parameter: CustomCTAParam.replaceInitialAction

replaceInitialAction( result: Initial, options?: ActionTypeConstructParam<Initial>['options'] ): ReplaceInitialActionType<Initial>

Returns instance of ReplaceInitialActionType

export class ReplaceInitialActionType<Initial extends CTAInitial,> extends ActionType<Initial> {

Returning ReplaceIntialActionType will produce the same outcome as dispatch.cta.replaceInitial

Example for CustomCTAParam.replaceInitialAction
const [state, dispatch] = useCTA({
	///...parameter
	actions: {
		specialReplaceInitial(ctaParam, payload?: boolean) {
			if (typeof payload === 'undefined') {
				// replace `initial` with this new state
				return ctaParam.replaceInitialAction({
					...ctaParam.previous,
					isFuzzy: true,
				})
			}

			// update state
			return {
				isFuzzy: payload,
			}
		},
	},
});

actions?.['customAction'] 1st Parameter: CustomCTAParam.resetAction

resetAction( result: Initial, options?: ActionTypeConstructParam<Initial>['options'] ): ResetActionType<Initial>

Returns instance of ResetActionType

export class ResetActionType<Initial extends CTAInitial,> extends ActionType<Initial> {

Returning ResetActionType will produce the same outcome as dispatch.cta.reset with payload parameter

Example for CustomCTAParam.resetAction
const [state, dispatch] = useCTA({
	///...parameter
	actions: {
		specialReplaceInitial(ctaParam, payload?: boolean) {
			if (typeof payload === 'undefined') {
				// replace `initial` and `state` with this new state
				return ctaParam.resetAction({
					...ctaParam.previous,
					isFuzzy: true,
				})
			}

			// update state
			return {
				isFuzzy: payload,
			}
		},
	},
});

actions?.['customAction'] 1st Parameter: CustomCTAParam.updateAction

updateAction( result: Partial<Initial>, options?: ActionTypeConstructParam<Initial>['options'] ): UpdateActionType<Initial>

Returns instance of UpdateActionType

export class UpdateActionType<Initial extends CTAInitial,> extends ActionType<Initial> {

Returning UpdateActionType will produce the same outcome as dispatch.cta.update

Example for CustomCTAParam.updateAction
const [state, dispatch] = useCTA({
	///...parameter
	actions: {
		specialReplaceInitial(ctaParam, payload?: boolean) {
			if (typeof payload === 'undefined') {
				// replace `initial` and `state` with this new state
				return ctaParam.updateAction({
					isFuzzy: true,
				})
			}

			// update state
			return ctaParam.previous
		},
	},
});

actions?.['customAction'] 2nd Parameter: payload

payloads can be:

  • undefined
  • optional or required where the value can be anything
  • Initial type
  • Partial<Initial> type

actions?.['customAction'] Return Type

Return type can be


Return Type

Array with two values: [state, dispatch] of type UseCTAReturnType

export type UseCTAReturnType<
Initial extends CTAInitial,
Actions = undefined,
> = [
Initial,
UseCTAReturnTypeDispatch<Initial, Actions>,
];

Typescript Definitions:


Return Type: state

Key/value object of type CTAInitial

The current state. During the first render, it’s set to the result of onInit or initial if onInit was not defined.


Return Type: dispatch

Is type UseCTAReturnTypeDispatch

export type UseCTAReturnTypeDispatch<
Initial extends CTAInitial,
Actions = undefined
> = DispatchCTA<Initial, Actions> & {
readonly cta: UseCTAReturnTypeDispatchCTA<Initial, Actions>,
readonly state: UseCTAReturnTypeDispatchState<Initial>
}

Typescript Definition:

A function with static read-only properties dispatch.state and dispatch.cta.

Triggers re-render when state or dispatch.state.initial changes

Parameters description will be covered by:


Return Type: dispatch.cta

readonly cta: UseCTAReturnTypeDispatchCTA<Initial, Actions>,

Has predefined actions

export type DispatchDefaultCTARecord<Initial extends CTAInitial> = Readonly<{
replace( payload: Initial | ( ( ctaParam: CTAParam<Initial> ) => Initial | undefined ) ): void;
replaceInitial( payload: Initial | ( ( ctaParam: CTAParam<Initial> ) => Initial | undefined ) ): void;
reset( payload?: Initial | ( ( ctaParam: CTAParam<Initial> ) => Initial | undefined ) ): void;
update( payload: Partial<Initial> | ( ( ctaParam: CTAParam<Initial> ) => Partial<Initial> | undefined ) , value?: undefined ): void;
update( key: keyof Initial, value: Initial[keyof Initial] ): void;
}>;

and custom actions if defined in actions?.['customAction'].

Typescript Definition:


Return Type: dispatch.cta?.['customAction']

Optional

Custom call to action for changing state

Parameters are based on the expected payload you defined them in Parameter: actions?.['customAction']


Return Type: dispatch.cta?.['customAction'] with payload parameter
Example for Return Type: `dispatch.cta?.['customAction']` with `payload` parameter
dispatch.cta.addToCount(5);

// Alias for
dispatch({
	type: 'addToCount',
	payload: 5
});

// or
dispatch.cta.addToCount((ctaParam) => {
	return ctaParam.previous.count + 10;
});

// Alias for
dispatch({
	type: 'addToCount',
	payload: (ctaParam) => {
		return ctaParam.previous.count + 10;
	}
});

// or
dispatch.cta.addToCount((ctaParam) => {
	// No re-render
	return;
});

// Alias for
dispatch({
	type: 'addToCount',
	payload: (ctaParam) => {
		// No re-render
		return;
	}
});

Return Type: dispatch.cta?.['customAction'] without parameter
Example for dispatch.cta?.['customAction'] without parameter
dispatch.cta.incrementCount();

// Alias for
dispatch({
	type: 'incrementCount',
});

Effects


Return Type: dispatch.cta.update

Overloaded function for partially changing state

update( payload: Partial<Initial> | ( ( ctaParam: CTAParam<Initial> ) => Partial<Initial> | undefined ) , value?: undefined ): void;
update( key: keyof Initial, value: Initial[keyof Initial] ): void;

Typescript Definition:


Return Type: dispatch.cta.update with payload parameter

Accepts partial key/values from Initial and updates state with partial change(s)

update( payload: Partial<Initial> | ( ( ctaParam: CTAParam<Initial> ) => Partial<Initial> | undefined ) , value?: undefined ): void;

Example
dispatch.cta.update({
	// partial `state` change
	search: 'update',
	count: 8,
});

// Alias for
dispatch({
	type: 'update',
	payload: {
		// partial `state` change
		search: 'update',
		count: 8,
	}
});

// or
dispatch.cta.update((ctaParam) => {
	return {
		// partial `state` change
		search: 'update',
		count: 8,
	};
});

// Alias for
dispatch({
	type: 'update',
	payload: (ctaParam) => {
		return {
			// partial `state` change
			search: 'update',
			count: 8,
		};
	}
});

// or
dispatch.cta.update((ctaParam) => {
	// No re-render
	return;
});

// Alias for
dispatch({
	type: 'update',
	payload: (ctaParam) => {
		// No re-render
		return;
	}
});

Return Type: dispatch.cta.update with key and value parameters

update( payload: keyof Initial, value: Initial[keyof Initial] ): void;

Accepts a key from Initial and a corresponding value type for that key from Initial[keyof Initial] and updates state with partial change

Example
dispatch.cta.update('seatch', 'update'); // partial `state` change

// Alias for
dispatch.cta.update({
	seatch: 'update',
});

Effects


Return Type: dispatch.cta.replace

Replace entire state with a new state.

replace( payload: Initial | ( ( ctaParam: CTAParam<Initial> ) => Initial | undefined ) ): void;

Typescript Definition:

Example for Return Type: dispatch.cta.replace
dispatch.cta.replace({
	// new `state`
	search: 'replace',
	isFuzzy: false,
	count: 8,
});

// Alias for
dispatch({
	type: 'replace',
	payload: {
		// new `state`
		search: 'replace',
		isFuzzy: false,
		count: 8,
	}
});

// or
dispatch.cta.replace((ctaParam) => {
	return {
		// new `state`
		search: 'replace',
		isFuzzy: false,
		count: 8,
	};
});

// Alias for
dispatch({
	type: 'replace',
	payload: (ctaParam) => {
		return {
			// new `state`
			search: 'replace',
			isFuzzy: false,
			count: 8,
		};
	}
});

// or
dispatch.cta.replace((ctaParam) => {
	// No re-render
	return;
});

// Alias for
dispatch({
	type: 'replace',
	payload: (ctaParam) => {
		// No re-render
		return;
	}
});

Effects


Return Type: dispatch.cta.replaceInitial

Replace entire dispatch.state.initial value with a new initial value.

replaceInitial( payload: Initial | ( ( ctaParam: CTAParam<Initial> ) => Initial | undefined ) ): void;

Typescript Definition:

Example for Return Type: dispatch.cta.replaceInitial
dispatch.cta.replaceInitial({
	// new `initial`
	search: 'replaceInitial',
	isFuzzy: true,
	count: 5,
});

// Alias for
dispatch({
	type: 'replaceInitial',
	payload: {
		// new `initial`
		search: 'replaceInitial',
		isFuzzy: true,
		count: 5,
	}
});

// or
dispatch.cta.replaceInitial((ctaParam) => {
	return {
		// new `initial`
		search: 'replaceInitial',
		isFuzzy: true,
		count: 5,
	};
});

// Alias for
dispatch({
	type: 'replaceInitial',
	payload: (ctaParam) => {
		return {
			// new `initial`
			search: 'replaceInitial',
			isFuzzy: true,
			count: 5,
		};
	}
});

// or
dispatch.cta.replaceInitial((ctaParam) => {
	// No re-render
	return;
});

// Alias for
dispatch({
	type: 'replaceInitial',
	payload: (ctaParam) => {
		// No re-render
		return;
	}
});

Effects


Return Type: dispatch.cta.reset

Overloaded function that differs in behavior when called with payload parameter and without parameter.

reset( payload?: Initial | ( ( ctaParam: CTAParam<Initial> ) => Initial | undefined ) ): void;

Typescript Definition:


Return Type: dispatch.cta.reset without parameter

Sets state equal to dispatch.state.initial

Example for Return Type: dispatch.cta.reset without parameter
dispatch.cta.reset();

// Alias for
dispatch({
	type: 'reset',
});

Effects


Return Type: dispatch.cta.reset with payload parameter

Sets state and dispatch.state.initial equal to payload

Example for Return Type: dispatch.cta.reset with payload parameter
dispatch.cta.reset({
	// new `state` and `initial`
	search: 'reset',
	isFuzzy: true,
	count: 10,
});

// Alias for
dispatch({
	type: 'reset',
	payload: {
		// new `state` and `initial`
		search: 'reset',
		isFuzzy: true,
		count: 10,
	}
});

// or
dispatch.cta.reset((ctaParam) => {
	return {
		// new `state` and `initial`
		search: 'reset',
		isFuzzy: true,
		count: 10,
	}; 
});

// Alias for
dispatch({
	type: 'reset',
	payload: (ctaParam) => {
		return {
			// new `state` and `initial`
			search: 'reset',
			isFuzzy: true,
			count: 10,
		};
	}
});

// or
dispatch.cta.reset((ctaParam) => {
	// No re-render
	return;
});

// Alias for
dispatch({
	type: 'reset',
	payload: (ctaParam) => {
		// No re-render
		return;
	}
});

Effects


Return Type: dispatch.state

readonly state: UseCTAReturnTypeDispatchState<Initial>

Is of type UseCTAReturnTypeDispatchState.

export type UseCTAReturnTypeDispatchState<Initial extends CTAInitial> = Readonly<{
changes: Readonly<Partial<Initial>> | null,
current: Readonly<Initial>,
initial: Readonly<Initial>,
previous: Readonly<Initial>
}>

This is extra data information that can be referenced for certain changes over time.

Typescript Definition:


Return Type: dispatch.state.current

current: Readonly<Initial>,

The value is equal to Return Type: state

Typescript Definition:


Return Type: dispatch.state.initial

initial: Readonly<Initial>,

Starts of equal to initial or the result of onInit.

It can be changed with dispatch.cta.replaceInitial or dispatch.cta.reset with payload parameter

Typescript Definition:


Return Type: dispatch.state.changes

changes: Readonly<Partial<Initial>> | null,

  • Partial<Initial>: When key/values of dispatch.state.current are not equal to dispatch.state.initial Example:
     	dispatch.state.initial /* = {
     		search: '',
     		isFuzzy: true,
     		count: 1,
     	} */
    
     	dispatch.state.current /* = {
     		search: 'current',
     		isFuzzy: true,
     		count: 1,
     	} */
    
     	dispatch.state.changes /* = {
     		search: 'current',
     	} */
    
     	if ( dispatch.state.changes ) {
     		console.log('state has changes')
     	}
  • null: When the key/values dispatch.state.initial and dispatch.state.current are equal. Example:
     	dispatch.state.initial /* = {
     		search: 'current',
     		isFuzzy: true,
     		count: 1,
     	} */
    
     	dispatch.state.current /* = {
     		search: 'current',
     		isFuzzy: true,
     		count: 1,
     	} */
    
     	dispatch.state.changes /* = null */
    
     	if ( !dispatch.state.changes ) {
     		console.log('No changes to state')
     	}

Typescript Definition:


Return Type: dispatch.state.previous

previous: Readonly<Initial>

Equal to the previous dispatch.state.current value.

Typescript Definition:


Typescript exports

export type { CTAInitial, } from './types/CTAInitial';
export type { CustomCTAParam, } from './types/CustomCTAParam';
export type { CustomCTAReturnType, } from './types/CustomCTAReturnType';
export type { UseCTAParameter, } from './types/UseCTAParameter';
export type { UseCTAReturnTypeDispatch, UseCTAReturnTypeDispatchState, } from './types/UseCTAReturnTypeDispatch';
export type { UseCTAReturnType, } from './types/UseCTAReturnType';


export type { CTAInitial, }

export type CTAInitial = Record<string | number, unknown>;


export type { CustomCTAParam, }

export type CustomCTAParam<Initial extends CTAInitial,> = UseCTAReturnTypeDispatchState<Initial> & {
replaceAction( result: Initial, options?: ActionTypeConstructParam<Initial>['options'] ): ReplaceActionType<Initial>
replaceInitialAction( result: Initial, options?: ActionTypeConstructParam<Initial>['options'] ): ReplaceInitialActionType<Initial>
resetAction( result: Initial, options?: ActionTypeConstructParam<Initial>['options'] ): ResetActionType<Initial>
updateAction( result: Partial<Initial>, options?: ActionTypeConstructParam<Initial>['options'] ): UpdateActionType<Initial>
};


export type { UseCTAParameter, }

See Parameter


export type { UseCTAReturnTypeDispatchState, }

See Return Type: dispatch

export type { UseCTAReturnTypeDispatch, }

See Return Type: dispatch


export type { UseCTAReturnType, }

See Return Type