State management with react-hook-use-cta
Tags: react, reactjsdevelopment
Published: 2024-06-29 00:31:23

How often do you see code that has a lot of `useState`?:

```tsx
import { useState } from 'react';

export default function Form() {
  const [firstName, setFirstName] = useState('Taylor');
  const [lastName, setLastName] = useState('Tonkin');
  const [likes, setLikes] = useState('Turtles');
  const [age, setAge] = useState(42);
  const [isAwesome, setIsAwesome] = useState(true);

  return (
    <>
      <input
        name="firstName"
        value={firstName}
        onChange={e => setFirstName(e.target.value)}
      />
      <input
        name="lastName"
        value={lastName}
        onChange={e => setLastName(e.target.value)}
      />
      <input
        name="likes"
        value={likes}
        onChange={e => setLikes(e.target.value)}
      />
      <input
        name="isAwesome"
        value={isAwesome}
        onChange={e => setIsAwesome(e.target.checked)}
        type="checkbox"
      />

      <button onClick={() => setAge((age) => age + 1}>
        Increment age
      </button>
      <button onClick={() => setAge((age) => age - 1}>
        Decrement age
      </button>

      <p>Hello, {firstName} {lastName}.</p>
      <p>You are {age}.</p>
      <p>And you like {likes}.</p>
      <p>{isAwesome ? 'Thanks for being awesome.' : 'You are still awesome.'</p>
    </>
  );
}
```

You could keep adding more `useState`s or have `useReducer` start handling your state.

What if you can consolidate all your `useState`s in a `useReducer` like manner?

# Try with `react-hook-use-cta`

`react-hook-use-cta` might be able to help you with that. Check this out:

```tsx
import { useCTA } from 'react-hook-use-cta';

export default function Form() {
  const [
    state,
    dispatch,
  ] = useCTA({
    initial: {
      firstName: 'Taylor',
      lastName: 'Tonkin',
      likes: 'Turtles',
      age: 42,
      isAwesome: true,
    }
  });

  return (
    <>
      <input
        name="firstName"
        value={state.firstName}
        onChange={e => dispatch.cta.update(
          'firstName',
          e.target.value
        )}
      />
      <input
        name="lastName"
        value={state.lastName}
        onChange={e => dispatch.cta.update(
          'lastName',
          e.target.value
        )}
      />
      <input
        name="likes"
        value={state.likes}
        onChange={e => dispatch.cta.update(
          'likes',
          e.target.value
        )}
      />
      <input
        name="isAwesome"
        value={state.isAwesome}
        onChange={e => dispatch.cta.update(
          'isAwesome',
          e.target.checked
        )}
        type="checkbox"
      />

      <button onClick={() => dispatch.cta.update(({current}) => ({ age: current.age + 1})}>
        Increment age
      </button>
      <button onClick={() => dispatch.cta.update(({current}) =>({ age: current.age - 1})}>
        Decrement age
      </button>

      <p>Hello, {state.firstName} {state.lastName}.</p>
      <p>You are {state.age}.</p>
      <p>And you like {state.likes}.</p>
      <p>{state.isAwesome ? 'Thanks for being awesome.' : 'You are still awesome.'</p>
    </>
  );
}
```

`useCTA` comes with an `update` action to merge updates to the state.
It also accepts a callback for accessing `current` state so you don't have to rely on closure. 

# Custom actions

Maybe you want to clean up a bit:

```tsx
import { useCTA } from 'react-hook-use-cta';

export default function Form() {
  const [
    state,
    dispatch,
  ] = useCTA({
    initial: {
      firstName: 'Taylor',
      lastName: 'Tonkin',
      likes: 'Turtles',
      age: 42,
      isAwesome: true,
    },
    actions: {
      onChangeText(ctaState, event) {
         return {
           [event.target.name]: event.target.value,
         };
      },
      onChangeCheckbox(ctaState, event) {
         return {
           [event.target.name]: event.target.checked,
         };
      },
      incrementAge(ctaState) {
         return {
           age: ctaState.current.age + 1,
         };
      },
      decrementAge(ctaState) {
         return {
           age: ctaState.current.age - 1, 
         };
      },
    }
  });

  return (
    <>
      <input
        name="firstName"
        value={state.firstName}
        onChange={dispatch.cta.onChangeText}
      />
      <input
        name="lastName"
        value={state.lastName}
        onChange={dispatch.cta.onChangeText}
      />
      <input
        name="likes"
        value={state.likes}
        onChange={dispatch.cta.onChangeText}
      />
      <input
        name="isAwesome"
        value={state.isAwesome}
        onChange={dispatch.cta.onChangeCheckbox}
        type="checkbox"
      />

      <button onClick={dispatch.cta.incrementAge}>
        Increment age
      </button>
      <button onClick={dispatch.cta.decrementAge}>
        Decrement age
      </button>

      <p>Hello, {state.firstName} {state.lastName}.</p>
      <p>You are {state.age}.</p>
      <p>And you like {state.likes}.</p>
      <p>{state.isAwesome ? 'Thanks for being awesome.' : 'You are still awesome.'</p>
    </>
  );
}
```

With custom actions, you can let action handlers take care of reading events or handle specific changes. 

These are some small examples of what `react-hook-use-cta` can do. I haven't even pointed out how you can use [createCTAContext](https://github.com/rafde/react-hook-use-cta?tab=readme-ov-file#createctacontext) for global state management.

Would you like to know more? Visit https://github.com/rafde/react-hook-use-cta

or leave a comment.

Thanks for reading.