DEV Community

loading...
Cover image for React Hooks Design Pattern

React Hooks Design Pattern

Jonathan Rodriguez
A mexican web developer
Updated on ・2 min read

I want to share with you one pattern to improve the readability and maintainability of your React components.

Why should we care about maintainability?
If our components are interdependent, not extensible, and not single-responsible, as our application grows in lines of code and complexity, our time to add new features or resolve bugs will also increase.

Let's say we have a form component, and we need to save its values into local storage as it changes.

function LocalStorageForm() {
  const [values, setValues] = useState({});

  const handleChange = (event) => {
    const { name, value } = event.target;
    setValues({ ...values, [name]: value });
  };

  return <Form values={values} onChange={handleChange} />;
}
Enter fullscreen mode Exit fullscreen mode

We might want to change the code into something like this, adding local storage synchronization logic inside the component:

function MessyLocalStorageForm() {
  const [values, setValues] = useState(() => {
    const storedValues = JSON.parse(localStorage.getItem('form'));
    return storedValues || {};
  });

  const handleChange = (event) => {
    const { name, value } = event.target;
    const updatedValues = { ...values, [name]: value };
    localStorage.setItem("form", JSON.stringify(updatedValues));
    setValues(updatedValues);
  };

  return <Form values={values} onChange={handleChange} />;
}
Enter fullscreen mode Exit fullscreen mode

It works, but is messy, what if we have another form where we want to also synchronize the values with local storage? Could we abstract the complexity of state management to reuse it multiple times? Yes, creating a custom hook only responsible for the local storage synchronization.

import { useState, useEffect } from "react";

function useLocalStorage(key, initialValue) {
  const [state, setState] = useState(() => {
    const value = localStorage.getItem(key);
    return JSON.parse(value) || initialValue;
  });

  useEffect(() => {
    const value = JSON.stringify(state);
    localStorage.setItem(key, value);
  }, [key, state]);

  return [state, setState];
}
Enter fullscreen mode Exit fullscreen mode

And now, all we need to do is go back to the first version of our code and just change the useState to our new custom hook useLocalStorage.

function LocalStorageForm() {
  const [values, setValues] = useLocalStorage('form', {});

  const handleChange = (event) => {
    const { name, value } = event.target;
    setValues({ ...values, [name]: value });
  };

  return <Form values={values} onChange={handleChange} />;
}
Enter fullscreen mode Exit fullscreen mode

Photo by Xavi Cabrera on Unsplash

Discussion (0)