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} />;
}
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} />;
}
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];
}
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} />;
}
Photo by Xavi Cabrera on Unsplash
Top comments (0)