DEV Community

Mike Romero
Mike Romero

Posted on

React hooks (intermedian): useStateForm

Note: make sure you understand the basics about react hooks, in the near future i will made posts about this.

Problem ๐Ÿ˜ฉ

As you may know managing the state of a form is a pain-hell, you need to create an object state, the methods to manage the changes in the inputs and be careful with the data you are changing. Some libraries like Formik abstract the state and provide some useful components that work with it. But as my known Formik is complicated to use and complicated to read, sometimes you will need the high-order component and sometimes the render-prop component but both options create code difficult to understand.

Solution

Use React Hooks ๐Ÿ˜

Context ๐Ÿ˜ฎ

If you like be updated with the new versions of React, for sure you heard about the React hooks and as the documentation says:

Hooks allow you to reuse stateful logic without changing your component hierarchy

React hooks are functions that allow us hook in the state and render phases of our component, and the best part is that we can create custom hooks to solve specific problems.

But how to use hooks to manage our form? well that's why i write this post ๐Ÿค“

Ok here we go! ๐Ÿ˜…

Think in the problem, first we need to abstract the state management into a custom hook and this custom hook needs to give us access to its state, in the below code we can see a simple implementation of the hooks to handle the state form.

function MyForm(props) {
  const [formState, setFormState] = useState({...});
  const handleChanges = (event) => {...};

  return (
    ...
    <input
      onChange={handleChanges}
      value={formState.prop1}
      name="prop1"
    />
    ...
  )
}
Enter fullscreen mode Exit fullscreen mode

To abstract the state create a custom hook called useStateForm there use the hook useState to handle the state and return it to keep it visible to the components. Also we need to pass the initialState of our form to the custom hook.

function useStateForm(initialState) {
  const [formState, setFormState] = useState(initialState);

  return formState
}
Enter fullscreen mode Exit fullscreen mode

Cool! the state is handle in our custom hook, but now we need some handleChange implementation to change our state ๐Ÿค”.

function useStateForm(initialState) {
  const [formState, setFormState] = useState(initialState);
  const handleChanges = (event) => {
    setFormState({
      ...formState,
      [event.target.name]: event.target.value
    })
  };
  return { formState, handleChanges };
}
Enter fullscreen mode Exit fullscreen mode

Awesome! our state is full abstracted, as you can see this is basically the same component without the render return statement and this is the power of hooks, now we can reuse common state functionality and share it between our components very easy, this will allow us save a lot of type coding. Now our component will looks like this:

function MyForm(props) {
  const {formState, handleChanges} = useStateForm({ prop1: '' });

  return (
    ...
    <input
      onChange={handleChanges}
      value={formState.prop1}
      name="prop1"
    />
    ...
  )
}
Enter fullscreen mode Exit fullscreen mode

Now we can go far away, what if instead of let the programmer the responsibility to set correctly the name of the inputs, our custom hook take care of that. What about validate the form state, or sometimes we need to format our raw data. Well all that you can find in my github project:

Top comments (1)

Collapse
 
jonathan1978 profile image
Jonathan Lawerh

I like the approach you have taken. I followed similar approach but I got stuck at a point where I am using a 3rd-party component and I'm unable to setFormState. Please see the 3rd-party component below and advise how I can set the formState with data from the component:

country={"us"}
error={hasError("phone")}
helperText={hasError("phone") ? formState.errors.phone[0] : null}
name="phone"
value={phone}
onChange={(phone) => setPhone(phone)}
/>