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

react-hook-use-cta

v2.0.0-pre.3

Published

A React hook for managing complex state with custom actions, history tracking, and type safety.

Downloads

229

Readme

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

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

Table of Contents


Installation

react-hook-use-cta

NPM

npm i react-hook-use-cta

Yarn

yarn add react-hook-use-cta

useCTA

Playground

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 search'),
		[]
	);

	/* Renders `update search` */
	return state.search;
}
import { useEffect, } from 'react';
import { useCTA, CustomCTAStateParam, CTAStateParam, } from 'react-hook-use-cta'

type ViewPropsInitial = { 
	search: string
	isFuzzy: boolean
	count: number
};

function View(props: { initial: ViewPropsInitial }) {
	const [
		state,
		dispatch,
	] = useCTA({
		initial: props.initial,
		onInit(initial) {
			return {
				...initial,
				search: 'onInit',
			}
		},
		actions: {
			// START: augment predefined actions
			updateInitial(ctaStateParam: CTAStateParam<ViewPropsInitial>, payload) {
				return payload;
			},
			reset(ctaStateParam: CTAStateParam<ViewPropsInitial>, payload) {
				return payload;
			},
			update(ctaStateParam: CTAStateParam<ViewPropsInitial>, payload) {
				return payload;
			},
			// END: augment predefined actions

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

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

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

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

Parameter

https://github.com/rafde/react-hook-use-cta/blob/193f711b632be93aaa693751e9bd7ed50ba098e6/src/types/UseCTAParameter.ts#L12-L19

[!NOTE] useCTA accepts an object, that is read once, with the following properties:

initial

[!IMPORTANT] A required object representing the initial state. Property values can be anything that strictDeepEqual from fast-equals supports.

Typescript:

onInit

https://github.com/rafde/react-hook-use-cta/blob/193f711b632be93aaa693751e9bd7ed50ba098e6/src/types/UseCTAParameter.ts#L18

[!NOTE] An optional callback for setting initial object on first render. It accepts the initial state object and returns a new initial state object.

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

function View() {
	const [
		state,
	] = useCTA({
		initial: {
			search: '',
		},
		onInit(initial) {
			return {
				...initial,
				search: 'onInit',
			}
		}
	});
	
	// renders `onInit`
	return state.search;
}

actions

[!NOTE] An optional object for augmenting call to actions or to create your own custom call to actions


Return

https://github.com/rafde/react-hook-use-cta/blob/193f711b632be93aaa693751e9bd7ed50ba098e6/src/types/UseCTAReturnType.ts#L7-L13

[!NOTE] An array with 2 values:

Current State

[!NOTE] A read-only object that is set by initial or result of onInit on first render. It is changed by most call to actions

Dispatcher

[!NOTE] A function used to make changes to the current state and trigger re-render. It also includes two properties:

cta

[!NOTE] A read-only object for accessing calls to actions to trigger state change. By default, it includes the following calls to actions https://github.com/rafde/react-hook-use-cta/blob/5ea1a69edc0a38e2aa4b870c08a95157628d914e/src/types/UseCTAReturnTypeDispatch.ts#L260-L292 type CTAPayloadCallbackParameter has the following properties: https://github.com/rafde/react-hook-use-cta/blob/68163de2b6f2dd1153c4dd703a45daba3dd9a495/src/types/CustomCTAStateParam.ts#L12-L15

state

https://github.com/rafde/react-hook-use-cta/blob/193f711b632be93aaa693751e9bd7ed50ba098e6/src/types/CustomCTAStateParam.ts#L12-L15

[!NOTE] A read-only object that can be used to reference additional states: You have access to the following states

state.current

https://github.com/rafde/react-hook-use-cta/blob/193f711b632be93aaa693751e9bd7ed50ba098e6/src/types/CustomCTAStateParam.ts#L13

[!NOTE] Equivalent to current state.

state.previous

https://github.com/rafde/react-hook-use-cta/blob/193f711b632be93aaa693751e9bd7ed50ba098e6/src/types/CustomCTAStateParam.ts#L14

[!NOTE] Starts off as null, is set to the previous state.current by the following actions:

state.initial

https://github.com/rafde/react-hook-use-cta/blob/193f711b632be93aaa693751e9bd7ed50ba098e6/src/types/CustomCTAStateParam.ts#L12

[!NOTE] Starts of equal to initial parameter or result of onInit on first render

state.previousInitial

[!NOTE] Starts off as null, is set to the previous state.initial by the following actions:

state.changes

https://github.com/rafde/react-hook-use-cta/blob/193f711b632be93aaa693751e9bd7ed50ba098e6/src/types/CustomCTAStateParam.ts#L15

[!NOTE] Starts of equal to null. When the property values of state.initial are equal to the current state, the value is null. Otherwise, it's equal to the difference in property values of state.initial and current state.

Dispatcher Parameter

Dispatcher function also accepts a parameter object with properties:

type

[!IMPORTANT] Required string. The value is a call to action or custom action name.

payload

[!WARNING] A parameter that a call to action can read. It's value depends on what it's corresponding call to action can accept.

args

https://github.com/rafde/react-hook-use-cta/blob/193f711b632be93aaa693751e9bd7ed50ba098e6/src/types/CustomCTAStateParam.ts#L16

[!NOTE] Optional unknown[] an augmented call to action or custom action based on how the action was defined.


Call to Actions

[!NOTE] Call to actions can be made through cta or dispatcher and augmented through actions parameter. There are call to actions available for immediate use.

[!IMPORTANT] When augmenting an existing call to action, the first parameter signature is CTAStateParam with the following properties: https://github.com/rafde/react-hook-use-cta/blob/193f711b632be93aaa693751e9bd7ed50ba098e6/src/types/CustomCTAStateParam.ts#L12-L16 The second parameter depends on what the corresponding call to action expects.

[!IMPORTANT] When using a callback as a payload, the first parameter signature is CTAPayloadCallbackParameter with the following properties: https://github.com/rafde/react-hook-use-cta/blob/0e8d359cf0f8dec77cc3d6d28de2c46ab0cc4027/src/types/CustomCTAStateParam.ts#L12-L15 return; or return undefined results in the call to action not triggering re-render. Otherwise, the returning value depends on what the corresponding call to action expects.

update

[!NOTE] Used to partially update the current state with a payload. Affects the following states:

| state | new state | |------------------------------------------------|-------------------------------------------------------------------------------------------------------------| | state.current | payload merged with old state.current | | state.previous | old state.current | | state.initial | no change | | state.previousInitial | no change | | state.changes | difference between state.initial and new state.current or null if equal |

How to update a single property

https://github.com/rafde/react-hook-use-cta/blob/6e82c86f58e637df321b27f116b68d8c514990ec/src/types/UseCTAReturnTypeDispatch.ts#L234-L238

dispatch.cta.update('search', 'update without option');
dispatch.cta.update('search', 'update with option', {hasOption: true});

How to update multiple properties

https://github.com/rafde/react-hook-use-cta/blob/0e8d359cf0f8dec77cc3d6d28de2c46ab0cc4027/src/types/UseCTAReturnTypeDispatch.ts#L282-L287

dispatch.cta.update({
	search: 'dispatch.cta.update',
	isFuzzy: true, 
});
dispatch.cta.update(
	{
		search: 'dispatch.cta.update with options',
		isFuzzy: true,
	},
	{
		updateWithOption: true,
	}
);
dispatch.cta.update(
	/**
	 * @param {CTAPayloadCallbackParameter<CTAInitial>} ctaPayloadCallbackParameter
	 * @returns {(CTAInitial | undefined)} returning `undefined` prevents action from triggering.
	 */
	(ctaPayloadCallbackParameter) => {
		if (ctaPayloadCallbackParameter.current.count > 10) {
			// This is a way to prevent an update from triggering.
			return;
		}
		
		return {
			search: 'dispatch.cta.update with callback',
			count: ctaPayloadCallbackParameter.current.count + 1,
		}
	}
);
dispatch.cta.update(
	/**
	 * @param {CTAPayloadCallbackParameter<CTAInitial>} ctaPayloadCallbackParameter
	 * @returns {(CTAInitial | undefined)} returning `undefined` prevents action from triggering.
	 */
	(ctaPayloadCallbackParameter) => {
		if (ctaPayloadCallbackParameter.current.count > 10) {
			// This is a way to prevent an update from triggering.
			return;
		}

		return {
			search: 'dispatch.cta.update with callback and options',
			count: ctaPayloadCallbackParameter.current.count + 1,
		}
	},
	{
		updateWithCallbackOption: true,
	}
);

https://github.com/rafde/react-hook-use-cta/blob/0e8d359cf0f8dec77cc3d6d28de2c46ab0cc4027/src/types/UseCTAReturnTypeDispatch.ts#L48-L54

dispatch({
	type: 'update',
	payload: {
		search: 'dispatch update',
		isFuzzy: true,
	},
});

dispatch({
	type: 'update',
	payload: {
		search: 'dispatch update with options',
		isFuzzy: true,
	},
	options: {
		dispatchUpdateWithOption: true,
	}
});

dispatch({
	type: 'update',
	/**
	 * @param {CTAPayloadCallbackParameter<CTAInitial>} ctaPayloadCallbackParameter
	 * @returns {(CTAInitial | undefined)} returning `undefined` prevents action from triggering.
	 */
	payload(ctaPayloadCallbackParameter) {
		if (ctaPayloadCallbackParameter.current.count > 10) {
			// This is a way to prevent an update from happening.
			return;
		}
	
		return {
			search: 'dispatch.cta.update with callback',
			count: ctaPayloadCallbackParameter.current.count + 1,
		}
	},
});


dispatch({
	type: 'update',
	/**
	 * @param {CTAPayloadCallbackParameter<CTAInitial>} ctaPayloadCallbackParameter
	 * @returns {(CTAInitial | undefined)} returning `undefined` prevents action from triggering.
	 */
	payload: (ctaPayloadCallbackParameter) => ({
		search: 'dispatch update with callback and options',
		count: ctaPayloadCallbackParameter.current.count + 1,
	}),
	options: {
		dispatchUpdateWithCallbackAndOption: true,
	}
});

How to augment update

https://github.com/rafde/react-hook-use-cta/blob/6e82c86f58e637df321b27f116b68d8c514990ec/src/types/UseCTAParameterActionsPredefinedRecord.ts#L8

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

const initial = {
	search: 'initial',
	isFuzzy: false,
	count: 0,
}

function View() {
	const [
		state,
		dispatch,
	] = useCTA({
		initial,
		actions: {
			/**
			 * @param {CTAStateParam<typeof initial>} ctaStateParam
			 * @param {typeof initial} payload
			 * @returns {(Partial<typeof initial> | void)} returning `void` prevents action from triggering.
			 */
			update(ctaStateParam, payload,) {
				const {
					current,
					options,
				} = ctaStateParam;
				let {
					count,
				} = payload;
				
				if (!Number.isSafeInteger(count)) {
					// if `count` is not a safe integer, prevent update 
					return;
				}

				// set count to current.count if allowNegativeCount is falsey and count is less than 0
				if (count < 0 && !options?.allowNegativeCount) {
					count = current.count;
				}

				return {
					...payload,
					count
				};
			}
		}
	});

	useEffect(
		() => {
			dispatch.cta.update(
				'count',
				-1,
				{
					allowNegativeCount: true
				}
			);
		},
		[
			dispatch,
		]
	);
	
	// will render `-1`
	return state.count;
}

updateInitial

[!NOTE] Set a new initial state with a payload. The idea of this action is in case there is new source data that should be used to compare changes with current state Affects the following states:

| state | new state | |------------------------------------------------|-------------------------------------------------------------------------------------------------------------| | state.current | no change | | state.previous | no change | | state.initial | payload | | state.previousInitial | old state.initial | | state.changes | difference between new state.initial and state.current or null if equal |

How to call updateInitial

https://github.com/rafde/react-hook-use-cta/blob/0e8d359cf0f8dec77cc3d6d28de2c46ab0cc4027/src/types/UseCTAReturnTypeDispatch.ts#L266-L271

dispatch.cta.updateInitial({
	search: 'dispatch.cta.updateInitial',
	isFuzzy: true,
	count: 10,
});
dispatch.cta.updateInitial(
	{
		search: 'dispatch.cta.updateInitial with option',
		isFuzzy: true,
		count: 10,
	},
	{
		isReplaceInitialWithOption: true,
	}
);
dispatch.cta.updateInitial(
	/**
	 * @param {CTAPayloadCallbackParameter<CTAInitial>} ctaPayloadCallbackParameter
	 * @returns {(CTAInitial | undefined)} returning `undefined` prevents action from triggering.
	 */
	(ctaPayloadCallbackParameter) => {
		if (ctaPayloadCallbackParameter.current.count > 10) {
			// This is a way to prevent updateInitial from triggering.
			return;
		}
		
		return {
			search: 'dispatch.cta.updateInitial with callback',
			isFuzzy: true,
			count: ctaPayloadCallbackParameter.current.count,
		}
	}
);
dispatch.cta.updateInitial(
	/**
	 * @param {CTAPayloadCallbackParameter<CTAInitial>} ctaPayloadCallbackParameter
	 * @returns {(CTAInitial | undefined)} returning `undefined` prevents action from triggering.
	 */
	(ctaPayloadCallbackParameter) => {
		if (ctaPayloadCallbackParameter.current.count > 10) {
			// This is a way to prevent updateInitial from triggering.
			return;
		}

		return {
			search: 'dispatch.cta.updateInitial with callback with options',
			isFuzzy: true,
			count: ctaPayloadCallbackParameter.current.count,
		}
	},
	{
		isReplaceInitialCallbackWithOption: true,
	}
);

https://github.com/rafde/react-hook-use-cta/blob/0e8d359cf0f8dec77cc3d6d28de2c46ab0cc4027/src/types/UseCTAReturnTypeDispatch.ts#L28-L34

dispatch({
	type: 'updateInitial',
	payload: {
		search: 'dispatch updateInitial',
		isFuzzy: true,
		count: 10,
	}
});

dispatch({
	type: 'updateInitial',
	payload: {
		search: 'dispatch updateInitial with options',
		isFuzzy: true,
		count: 10,
	},
	options: {
		isReplacingWithOption: true,
	}
});

dispatch({
	type: 'updateInitial',
	/**
	 * @param {CTAPayloadCallbackParameter<CTAInitial>} ctaPayloadCallbackParameter
	 * @returns {(CTAInitial | undefined)} returning `undefined` prevents action from triggering.
	 */
	payload(ctaPayloadCallbackParameter) {
		if (ctaPayloadCallbackParameter.current.count > 10) {
			// This is a way to prevent updateInitial from triggering.
			return;
		}
	
		return {
			search: 'dispatch.cta.updateInitial with callback',
			isFuzzy: true,
			count: ctaPayloadCallbackParameter.current.count,
		}
	},
});

dispatch({
	type: 'updateInitial',
	/**
	 * @param {CTAPayloadCallbackParameter<CTAInitial>} ctaPayloadCallbackParameter
	 * @returns {(CTAInitial | undefined)} returning `undefined` prevents action from triggering.
	 */
	payload: (ctaPayloadCallbackParameter) => ({
		search: 'dispatch updateInitial with callback and options',
		isFuzzy: true,
		count: ctaPayloadCallbackParameter.current.count + 1,
	}),
	options: {
		isCallbackReplacingWithOption: true,
	}
});

How to augment updateInitial

https://github.com/rafde/react-hook-use-cta/blob/6e82c86f58e637df321b27f116b68d8c514990ec/src/types/UseCTAParameterActionsPredefinedRecord.ts#L6

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

const initial = {
	search: 'initial',
	isFuzzy: false,
	count: 0,
}

function View() {
	const [
		state,
		dispatch,
	] = useCTA({
		initial,
		actions: {
			/**
			 * @param {CTAStateParam<typeof initial>} ctaStateParam
			 * @param {typeof initial} payload
			 * @returns {(typeof initial | undefined)} returning `undefined` prevents action from triggering.
			 */
			updateInitial(ctaStateParam, payload) {
				const {
					current,
					options,
				} = ctaStateParam;
				let {
					count,
				} = payload;

				if (Number.isSafeInteger(count)) {
					// prevent updateInitial if count is not a safe integer
					return;
				}

				// set count to current.count if allowNegativeCount is falsey and count is less than 0
				if (count < 0 && !options?.allowNegativeCount) {
					count = current.count;
				}

				return {
					...payload,
					count
				};
			}
		}
	});

	useEffect(
		() => {
			dispatch.cta.updateInitial(
				{
					search: 'updateInitial',
					isFuzzy: true,
					count: 10,
				},
				{
					allowNegativeCount: true,
				}
			);
		},
		[
			dispatch,
		]
	);
	
	// will render `10`
	return dispatch.state.initial.count;
}

reset

[!NOTE] reset is a special action that has 2 behaviors:

How to call reset without a payload to replace current state with initial state

https://github.com/rafde/react-hook-use-cta/blob/eee697a4487ed4a6cfe830ceb6057402fa0a7b07/src/types/UseCTAReturnTypeDispatch.ts#L265-L268

[!NOTE] If no payload is sent, then the current state will be replaced the initial state. Affects the following states:

| state | new state | |----------------------------------|-----------------------------------------------------------------------------------| | state.current | state.initial | | state.previous | old state.current | | state.initial | no change | | state.initial | no change | | state.changes | null since state.initial equals state.current |

// sets current state = to initial state
dispatch.cta.reset();
// sets current state = to initial state
dispatch.cta.reset(
	undefined,
	{
		resetWithOption: true,
	}
);

https://github.com/rafde/react-hook-use-cta/blob/6e82c86f58e637df321b27f116b68d8c514990ec/src/types/UseCTAReturnTypeDispatch.ts#L26-L32

dispatch({
	type: 'reset'
});

dispatch({
	type: 'reset',
	options: {
		resetWithOption: true,
	}
});

How to call reset with payload

https://github.com/rafde/react-hook-use-cta/blob/0e8d359cf0f8dec77cc3d6d28de2c46ab0cc4027/src/types/UseCTAReturnTypeDispatch.ts#L276-L281

[!NOTE] If a payload is sent, then the initial state and the current state will be replaced with the payload. Affects the following states:

| state | new state | |------------------------------------------|-----------------------------------------------------------------------------------| | state.current | payload | | state.previous | old state.current | | state.initial | payload | | previousInitial | old state.initial | | state.changes | null since state.initial equals state.current |

// sets current state and initial state equal to payload
dispatch.cta.reset({
	search: 'dispatch.cta.reset',
	isFuzzy: true,
	count: 10,
});
// sets current state and initial state equal to payload
dispatch.cta.reset(
	{
		search: 'dispatch.cta.reset with options',
		isFuzzy: true,
		count: 10,
	},
	{
		resetInitialWithOption: true,
	}
);
// sets current state and initial state equal to payload
dispatch.cta.reset(
	/**
	 * @param {CTAPayloadCallbackParameter<CTAInitial>} ctaPayloadCallbackParameter
	 * @returns {(CTAInitial | undefined)} returning `undefined` prevents action from triggering.
	 */
	(ctaPayloadCallbackParameter) => {
		if (ctaPayloadCallbackParameter.current.count > 10) {
			// prevent reset from triggering
			return;
		}
	
		// sets current state and initial state equal to payload
		return {
			search: 'dispatch.cta.reset with callback',
			isFuzzy: true,
			count: ctaPayloadCallbackParameter.current.count,
		}
	}
);
// sets current state and initial state equal to payload
dispatch.cta.reset(
	/**
	 * @param {CTAPayloadCallbackParameter<CTAInitial>} ctaPayloadCallbackParameter
	 * @returns {(CTAInitial | undefined)} returning `undefined` prevents action from triggering.
	 */
	(ctaPayloadCallbackParameter) => {
		if (ctaPayloadCallbackParameter.current.count > 10) {
			// prevent reset from triggering
			return;
		}
		
		return {
			search: 'dispatch.cta.reset with callback with options',
			isFuzzy: true,
			count: ctaPayloadCallbackParameter.current.count + 1,
		}
	},
	{
		resetCallbackWithOption: true,
	}
);

https://github.com/rafde/react-hook-use-cta/blob/0e8d359cf0f8dec77cc3d6d28de2c46ab0cc4027/src/types/UseCTAReturnTypeDispatch.ts#L36-L46

dispatch({
	type: 'reset',
	payload: {
		search: 'dispatch reset',
		isFuzzy: true,
		count: 10,
	}
});

dispatch({
	type: 'reset',
	payload: {
		search: 'dispatch reset with option',
		isFuzzy: true,
		count: 10,
	},
	options: {
		resetInitialWithOption: true,
	}
});

dispatch({
	type: 'reset',
	/**
	 * @param {CTAPayloadCallbackParameter<CTAInitial>} ctaPayloadCallbackParameter
	 * @returns {(CTAInitial | undefined)} returning `undefined` prevents action from triggering.
	 */
	payload(ctaPayloadCallbackParameter) {
		if (ctaPayloadCallbackParameter.current.count > 10) {
			// prevent reset from triggering
			return;
		}

		// sets current state and initial state equal to payload
		return {
			search: 'dispatch.cta.reset with callback',
			isFuzzy: true,
			count: ctaPayloadCallbackParameter.current.count,
		}
	},
});

dispatch({
	type: 'reset',
	/**
	 * @param {CTAPayloadCallbackParameter<CTAInitial>} ctaPayloadCallbackParameter
	 * @returns {(CTAInitial | void)} returning `undefined` prevents action from triggering.
	 */
	payload(ctaPayloadCallbackParameter) {
		if (ctaPayloadCallbackParameter.current.count > 10) {
			// prevent reset from triggering
			return;
		}

		return {
			search: 'dispatch.cta.reset with callback',
			isFuzzy: true,
			count: ctaPayloadCallbackParameter.current.count + 1,
		}
	},
	options: {
		resetCallbackWithOption: true,
	}
});

How to augment reset

https://github.com/rafde/react-hook-use-cta/blob/6e82c86f58e637df321b27f116b68d8c514990ec/src/types/UseCTAParameterActionsPredefinedRecord.ts#L7

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

const initial = {
	search: 'initial',
	isFuzzy: false,
	count: 0,
}

function View() {
	const [
		state,
		dispatch,
	] = useCTA({
		initial,
		actions: {
			/**
			 * @param {CTAStateParam<typeof initial>} ctaStateParam
			 * @param {typeof initial=} payload - optional
			 * @returns {(typeof initial | void)} returning `void` prevents action from triggering.
			 */
			reset(ctaStateParam, payload,) {
				const {
					current,
					options,
				} = ctaStateParam;
				
				// You must handle `payload` that is `undefined`
				if (!payload) {
					// this will set current = initial
					return ctaStateParam.initial;
				}
				
				let {
					count,
				} = payload;
				
				if (!Number.isSafeInteger(count)) {
					// prevent reset from triggering
					return;
				}
				
				// set count to current.count if allowNegativeCount is falsey and count is less than 0
				if (count < 0 && !options?.allowNegativeCount) {
					count = current.count;
				}

				return {
					...payload,
					count,
				};
			}
		}
	});

	useEffect(
		() => {
			dispatch.cta.reset(
				{
					search: 'reset',
					isFuzzy: true,
					count: -1,
				},
				{
					allowNegativeCount: true,
				}
			);
		},
		[
			dispatch,
		]
	);
	
	// will render `-1`
	return state.count;
}

Custom Actions

https://github.com/rafde/react-hook-use-cta/blob/eee697a4487ed4a6cfe830ceb6057402fa0a7b07/src/types/UseCTAParameterActionsRecordProp.ts#L6-L14

[!NOTE] When the available actions aren't enough, you can define your own specialized custom actions using action behaviors.

[!IMPORTANT] All custom action callbacks receive a CustomCTAStateParam as their first parameter with the following properties. https://github.com/rafde/react-hook-use-cta/blob/eee697a4487ed4a6cfe830ceb6057402fa0a7b07/src/types/CustomCTAStateParam.ts#L12-L20 The second parameter depends on what you want sent as a payload

[!WARNING] Augmented existing call to actions become the default behavior when using them in custom actions. To use non-augmented behavior, provide {useDefault: true} option as the second parameter. https://github.com/rafde/react-hook-use-cta/blob/5ea1a69edc0a38e2aa4b870c08a95157628d914e/src/internal/ActionTypes.ts#L8

How to define and call custom action as update behavior

[!IMPORTANT] All custom actions behave as an update when returning a Partial<CTAInitial>.

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

function View() {
	const [
		state,
		dispatch,
	] = useCTA({
		initial: {
			count: 0,
		},
		actions: {
			addToCount(ctaParam, value: number) {
				return {
					count: ctaParam.previous.count + value,
				}
			},
			incrementCount(ctaParam) {
				return {
					count: ctaParam.current.count + 1,
				}
			},
		}
	});

	useEffect(
		() => {
			dispatch.cta.incrementCount();
			dispatch.cta.addToCount(3)
		},
		[]
	);

	// renders `4`
	return state.count;
}
import { useEffect, } from 'react';
import { useCTA, } from 'react-hook-use-cta'

function View() {
	const [
		state,
		dispatch,
	] = useCTA({
		initial: {
			count: 0,
			search: '',
		},
		actions: {
			update(ctaParam, payload) {
				const {
					count = ctaParam.current.count,
				} = payload;
				return {
					...payload,
					count: count + 1
				};
			},
			multiplyCount(ctaParam, value: number) {
				return ctaParam.updateAction(
					{
						count: ctaParam.current.count * value
					},
					{
						// don't update using augmented behavior.
						useDefault: true,
					}
				)
			},
		}
	});

	useEffect(
		() => {
			dispatch.cta.update('search', 'update');
			dispatch.cta.multiplyCount(7)
		},
		[]
	);

	// renders `7`
	return state.count;
}

How to define and call custom action as updateInitial behavior

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

function View() {
	const [
		state,
		dispatch,
	] = useCTA({
		initial: {
			count: 0,
			search: '',
			isFuzzy: false,
		},
		actions: {
			sourceSync(ctaParam,) {
				return ctaParam.updateInitialAction(
					{
						count: 13,
						search: 'sourceSync',
						isFuzzy: true,
					}
				)
			},
		}
	});

	useEffect(
		() => {
			dispatch.cta.sourceSync();
		},
		[]
	);

	return <>
		{/* renders `13` */}
		<div>{dispatch.state.initial.count}</div>
		{/* renders `sourceSync` */}
		<div>{dispatch.state.initial.search}</div>
		{/* renders `true` */}
		<div>{dispatch.state.initial.isFuzzy}</div>
		{/* renders `0` */}
		<div>{state.count}</div>
		{/* renders `` */}
		<div>{state.search}</div>
		{/* renders `false` */}
		<div>{state.isFuzzy}</div>
	</>;
}

How to define and call custom action as reset behavior

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

function View() {
	const [
		state,
		dispatch,
	] = useCTA({
		initial: {
			count: 0,
			search: '',
			isFuzzy: false,
		},
		actions: {
			sync(ctaParam,) {
				return ctaParam.resetAction(
					{
						count: 13,
						search: 'sync',
						isFuzzy: true,
					}
				)
			},
		}
	});

	useEffect(
		() => {
			dispatch.cta.sync();
		},
		[]
	);

	return <>
		{/* renders `null` */}
		<div>{dispatch.state.changes}</div>
		{/* renders `13` */}
		<div>{dispatch.state.initial.count}</div>
		{/* renders `sync` */}
		<div>{dispatch.state.initial.search}</div>
		{/* renders `true` */}
		<div>{dispatch.state.initial.isFuzzy}</div>
		{/* renders `13` */}
		<div>{state.count}</div>
		{/* renders `sync` */}
		<div>{state.search}</div>
		{/* renders `true` */}
		<div>{state.isFuzzy}</div>
	</>;
}

createCTAContext

Playground

[!NOTE] Combines useCTA with React createContext and useContext. Accepts the same parameters as useCTA: https://github.com/rafde/react-hook-use-cta/blob/eee697a4487ed4a6cfe830ceb6057402fa0a7b07/src/types/UseCTAParameter.ts#L12-L19

import { createCTAContext, } from 'react-hook-use-cta'

export const GlobalContext = createCTAContext({
	initial: {
		search: 'initial',
		isFuzzy: false,
		count: 0,
	},
});

Returns an object the following key/value:

CTAProvider

[!NOTE] Provider to wrap the app or component for context. It accepts props:

import GlobalContext from './globalContext';
import { GlobalCountView, } from './GlobalCountView'
import { GlobalCountButton, } from './GlobalCountButton'

const appInitial = {
	search: 'app',
	isFuzzy: true,
	count: 11,
}

export function App() {
	return <GlobalContext.CTAProvider initial={appInitial}>
		<GlobalCountButton/>
		<GlobalCountView/>
	</GlobalContext.CTAProvider>;
}

useCTAStateContext

[!NOTE] Hook that returns the current state

import { GlobalContext, } from './globalContext';

const {
	useCTAStateContext
} = GlobalContext;

export function GlobalCountView() {
	const globalState = useCTAStateContext();
	return <div>
		{globalState.count}
	</div>;
}

useCTADispatchContext

[!NOTE] Hook that returns cta dispatcher. Returns null if called outside CTAProvider

import { useCallback, } from 'react';
const {
	useCTADispatchContext
} = GlobalContext;

export function GlobalCountButton() {
	const globalDispatch = useCTADispatchContext();
	const onClick = useCallback(
		() => {
			globalDispatch.cta.update((state) => {
				return {
					count: state.current.count + 1,
				}
			})
		},
		[
			globalDispatch
		]
	)
	return <button {...{
		onClick,
	}}>
		Update count:
	</button>;
}

returnActionsType

https://github.com/rafde/react-hook-use-cta/blob/5ea1a69edc0a38e2aa4b870c08a95157628d914e/src/index.ts#L36-L41

[!NOTE] In case you need to define actions parameter from a variable, this function can help infer actions type

import { returnActionsType, } from 'react-hook-use-cta';

const initial = {
	search: 'initial',
	isFuzzy: false,
	count: 0,
};
const actions = returnActionsType(
	initial,
	{
		setSearch(state, search: string) {
			return {
				search
			}
		}
	}
);

Typescript exports

https://github.com/rafde/react-hook-use-cta/blob/5ea1a69edc0a38e2aa4b870c08a95157628d914e/src/index.ts#L45-L59

export type { CTAInitial, }

https://github.com/rafde/react-hook-use-cta/blob/9e9206f1ff06e2de5adcde5d107d9d847e210063/src/types/CTAInitial.ts#L1

export type { UseCTAParameter, }

https://github.com/rafde/react-hook-use-cta/blob/5ea1a69edc0a38e2aa4b870c08a95157628d914e/src/types/UseCTAParameter.ts#L12-L19

export type { UseCTAReturnType, }

https://github.com/rafde/react-hook-use-cta/blob/65c3b53dd5d51812e3ffc111ba23c4bc84f614ee/src/types/UseCTAReturnType.ts#L4-L10

export type { UseCTAReturnTypeDispatch, }

https://github.com/rafde/react-hook-use-cta/blob/65c3b53dd5d51812e3ffc111ba23c4bc84f614ee/src/types/UseCTAReturnTypeDispatch.ts#L261-L267

export type { CTAPayloadCallbackParameter, }

https://github.com/rafde/react-hook-use-cta/blob/296c3136ac2d12940c48be06ea20ee80f748c5fa/src/types/UseCTAReturnTypeDispatch.ts#L13-L18

export type { CustomCTAStateParam, }

https://github.com/rafde/react-hook-use-cta/blob/6e82c86f58e637df321b27f116b68d8c514990ec/src/types/CustomCTAStateParam.ts#L11-L21

export type { CTAStateParam, }

https://github.com/rafde/react-hook-use-cta/blob/6c8def7fcd104b6ae0d25627eaad1a1e35a3a391/src/types/CTAStateParam.ts#L4-L9