react-hook-use-cta
v1.1.0
Published
A somewhat flexible react hook alternative to `React.useReducer`. Written in Typescript.
Downloads
20
Maintainers
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
- useCTA
- createCTAContext
Installation
react-hook-use-cta
NPM
npm i react-hook-use-cta
Yarn
yarn add react-hook-use-cta
useCTA
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
replace(ctaStateParam: CTAStateParam<ViewPropsInitial>, payload) {
return payload;
},
replaceInitial(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.initial.search}</div>
<div>{dispatch.state.changes?.search}</div>
<div>{dispatch.state.previous.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 stateobject
and returns a new initial stateobject
.
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-L292type 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.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.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 of equal to initial parameter or result of onInit on first render. Is set to the previous current state if it changes.
- update
- replace
- replaceInitial
- reset
- custom update action
- custom replace action
- custom replaceInitial action
- custom reset action
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 isnull
. Otherwise, it's equal to the difference in property values of state.initial and current state.
- update
- replace
- replaceInitial
- reset
- custom update action
- custom replace action
- custom replaceInitial action
- custom reset action
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.
options
https://github.com/rafde/react-hook-use-cta/blob/193f711b632be93aaa693751e9bd7ed50ba098e6/src/types/CustomCTAStateParam.ts#L16
[!NOTE] Optional key/value
object
that an augmented call to action or custom action may read.
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-L15return;
orreturn 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 apayload
. Affects the following states:
| state | new state |
|----------------------------|-------------------------------------------------------------------------------------------------|
| current | payload
merged with old current |
| initial | no change |
| previous | old current |
| changes | difference between initial and new 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;
}
replace
[!NOTE] Used to
replace
current state with apayload
. Affects the following states:
| state | new state |
|----------------------------|-------------------------------------------------------------------------------------------------|
| current | payload
|
| initial | no change |
| previous | old current |
| changes | difference between initial and new current or null
if equal |
How to replace
the entire state
https://github.com/rafde/react-hook-use-cta/blob/0e8d359cf0f8dec77cc3d6d28de2c46ab0cc4027/src/types/UseCTAReturnTypeDispatch.ts#L260-L265
dispatch.cta.replace({
search: 'dispatch.cta.replace',
isFuzzy: true,
count: 10,
});
dispatch.cta.replace(
{
search: 'dispatch.cta.replace with options',
isFuzzy: true,
count: 10,
},
{
isReplacingWithOption: true,
}
);
dispatch.cta.replace(
/**
* @param {CTAPayloadCallbackParameter<Initial>} ctaPayloadCallbackParameter
* @returns {(CTAInitial | undefined)} returning `undefined` prevents triggering the action
*/
(ctaPayloadCallbackParameter) => {
const count = ctaPayloadCallbackParameter.current.count;
// This is a way to prevent replace from happening.
if (count > 10) {
return;
}
return {
search: 'dispatch.cta.replace with callback',
isFuzzy: true,
count,
}
}
);
dispatch.cta.replace(
/**
* @param {CTAPayloadCallbackParameter<Initial>} ctaPayloadCallbackParameter
* @returns {(CTAInitial | undefined)} returning `undefined` prevents triggering the action
*/
(ctaPayloadCallbackParameter) => ({
search: 'dispatch.cta.replace with callback and options',
isFuzzy: true,
count: ctaPayloadCallbackParameter.current.count + 1,
}),
{
isCallbackReplacingWithOption: true,
}
);
https://github.com/rafde/react-hook-use-cta/blob/0e8d359cf0f8dec77cc3d6d28de2c46ab0cc4027/src/types/UseCTAReturnTypeDispatch.ts#L20-L26
dispatch({
type: 'replace',
payload: {
search: 'dispatch replace',
isFuzzy: true,
count: 10,
}
});
dispatch({
type: 'replace',
payload: {
search: 'dispatch replace with option',
isFuzzy: true,
count: 10,
},
options: {
isReplacingWithOption: true,
}
});
dispatch({
type: 'replace',
/**
* @param {CTAPayloadCallbackParameter<Initial>} ctaPayloadCallbackParameter
* @returns {(CTAInitial | undefined)} returning `undefined` prevents action from triggering.
*/
payload: (ctaPayloadCallbackParameter) => {
const count = ctaPayloadCallbackParameter.current.count;
if (count > 10) {
// This is a way to prevent replace from triggering.
return;
}
return {
search: 'dispatch.cta.replace with callback',
isFuzzy: true,
count,
}
},
});
dispatch({
type: 'replace',
/**
* @param {CTAPayloadCallbackParameter<Initial>} ctaPayloadCallbackParameter
* @returns {(CTAInitial | undefined)} returning `undefined` prevents action from triggering.
*/
payload: (ctaPayloadCallbackParameter) => {
const count = ctaPayloadCallbackParameter.current.count;
if (count > 10) {
// This is a way to prevent replace from triggering.
return;
}
return {
search: 'dispatch.cta.replace with callback with options',
isFuzzy: true,
count,
}
},
options: {
isCallbackReplacingWithOption: true,
}
});
How to augment replace
https://github.com/rafde/react-hook-use-cta/blob/6e82c86f58e637df321b27f116b68d8c514990ec/src/types/UseCTAParameterActionsPredefinedRecord.ts#L5
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
* @return {(typeof initial | void)} returning `void` prevents action from triggering.
*/
replace(ctaStateParam, payload,) {
const {
current,
options,
} = ctaStateParam;
let {
count,
} = payload;
if (Number.isSafeInteger(count)) {
// if count is not a safe integer, prevent triggering replace
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.replace(
{
search: 'replace',
isFuzzy: true,
count: 10,
},
{
allowNegativeCount: true,
}
);
},
[
dispatch,
]
);
// will render `-1`
return state.count;
}
replaceInitial
[!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 |
|----------------------------|-------------------------------------------------------------------------------------------------|
| current | no change |
| initial | payload
|
| previous | no change |
| changes | difference between new initial and current or null
if equal |
How to call replaceInitial
https://github.com/rafde/react-hook-use-cta/blob/0e8d359cf0f8dec77cc3d6d28de2c46ab0cc4027/src/types/UseCTAReturnTypeDispatch.ts#L266-L271
dispatch.cta.replaceInitial({
search: 'dispatch.cta.replaceInitial',
isFuzzy: true,
count: 10,
});
dispatch.cta.replaceInitial(
{
search: 'dispatch.cta.replaceInitial with option',
isFuzzy: true,
count: 10,
},
{
isReplaceInitialWithOption: true,
}
);
dispatch.cta.replaceInitial(
/**
* @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 replaceInitial from triggering.
return;
}
return {
search: 'dispatch.cta.replaceInitial with callback',
isFuzzy: true,
count: ctaPayloadCallbackParameter.current.count,
}
}
);
dispatch.cta.replaceInitial(
/**
* @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 replaceInitial from triggering.
return;
}
return {
search: 'dispatch.cta.replaceInitial 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: 'replaceInitial',
payload: {
search: 'dispatch replaceInitial',
isFuzzy: true,
count: 10,
}
});
dispatch({
type: 'replaceInitial',
payload: {
search: 'dispatch replaceInitial with options',
isFuzzy: true,
count: 10,
},
options: {
isReplacingWithOption: true,
}
});
dispatch({
type: 'replaceInitial',
/**
* @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 replaceInitial from triggering.
return;
}
return {
search: 'dispatch.cta.replaceInitial with callback',
isFuzzy: true,
count: ctaPayloadCallbackParameter.current.count,
}
},
});
dispatch({
type: 'replaceInitial',
/**
* @param {CTAPayloadCallbackParameter<CTAInitial>} ctaPayloadCallbackParameter
* @returns {(CTAInitial | undefined)} returning `undefined` prevents action from triggering.
*/
payload: (ctaPayloadCallbackParameter) => ({
search: 'dispatch replaceInitial with callback and options',
isFuzzy: true,
count: ctaPayloadCallbackParameter.current.count + 1,
}),
options: {
isCallbackReplacingWithOption: true,
}
});
How to augment replaceInitial
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.
*/
replaceInitial(ctaStateParam, payload) {
const {
current,
options,
} = ctaStateParam;
let {
count,
} = payload;
if (Number.isSafeInteger(count)) {
// prevent replaceInitial 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.replaceInitial(
{
search: 'replaceInitial',
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 |
|----------------------------|-----------------------------------------------------------------------|
| current | initial |
| initial | no change |
| previous | old current |
| changes | null
since initial equals 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 thepayload
. Affects the following states:
| state | new state |
|----------------------------|-----------------------------------------------------------------------|
| current | payload
|
| initial | payload
|
| previous | old current |
| changes | null
since initial equals 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: 'replace',
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 replace
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: {
preset(ctaParam,) {
return ctaParam.replaceAction(
{
count: !ctaParam.current.search ? 11 : ctaParam.current.count,
search: 'preset',
isFuzzy: true,
}
)
},
}
});
useEffect(
() => {
dispatch.cta.preset();
},
[]
);
return <>
{/* renders `11` */}
<div>{state.count}</div>
{/* renders `preset` */}
<div>{state.search}</div>
{/* renders `true` */}
<div>{state.isFuzzy}</div>
</>;
}
How to define and call custom action as replaceInitial
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.replaceInitialAction(
{
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
[!NOTE] Combines
useCTA
with ReactcreateContext
anduseContext
. Accepts the same parameters asuseCTA
: 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 export
s
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