DEV Community

Rafael De Leon
Rafael De Leon

Posted on

State management with react-hook-use-cta

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

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>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

You could keep adding more useStates or have useReducer start handling your state.

What if you can consolidate all your useStates 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:

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>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

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:

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>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

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 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.

Top comments (0)