Avoid useState()
In some situations & use-cases, using useState() is not the optimal choice. There are preferrable alternatives!
React's useState
hook is a cornerstone of state management in functional components, enabling developers to maintain local state with ease. However, there are scenarios where relying solely on useState
might not be the most efficient or appropriate approach for managing stateful data. In this article, we'll explore alternative strategies that can enhance both user experience and application performance, without compromising the core principles of React.
Understanding useState()
Before delving into alternatives, it's crucial to acknowledge the importance of useState
. It allows components to react to data changes, ensuring the UI stays in sync with the underlying data model. This is particularly useful in dynamic applications where the UI needs to update frequently in response to user interactions or other events.
// Example of useState in a simple counter componentfunction Counter() {const [count, setCount] = useState(0);return (<div><p>You clicked {count} times</p><button onClick={() => setCount(count + 1)}>Click me</button></div>);}
When to Rethink useState()
Despite its utility, there are situations where useState
might not be the optimal choice:
Form Data Handling
Commonly, forms in React are managed by linking input
elements to state variables, updating the state with every keystroke. While this approach works, it can lead to unnecessary re-renders and performance issues, especially in large forms or complex applications.
function LoginForm() {const [email, setEmail] = useState('');return <inputtype="email"onChange={(event) => setEmail(event.target.value)}value={email} />}
Instead of binding input fields to state variables, consider using uncontrolled components with the useRef
hook. This approach involves reading the input values directly from the DOM at the time of form submission, reducing the number of re-renders and improving performance.
// Example of using useRef in a formfunction LoginForm() {const emailRef = useRef(null);const passwordRef = useRef(null);function handleSubmit(event) {event.preventDefault();const email = emailRef.current.value;const password = passwordRef.current.value;// Process login with email and password};return (<form onSubmit={handleSubmit}><input ref={emailRef} type="email" placeholder="Email" /><input ref={passwordRef} type="password" placeholder="Password" /><button type="submit">Login</button></form>);}
Utilizing Browser Features for Form Submission
Leveraging the browser's native form submission capabilities can further streamline form handling. By accessing the form data from the submission event, you can eliminate the need for useState
or useRef
altogether, relying on the browser's efficiency in managing form inputs.
// Example of using browser's form submissionfunction LoginForm() {function handleSubmit(event) {event.preventDefault();const formData = new FormData(event.target);const email = formData.get('email');const password = formData.get('password');// Process login with email and password};return (<form onSubmit={handleSubmit}><input name="email" type="email" placeholder="Email" /><input name="password" type="password" placeholder="Password" /><button type="submit">Login</button></form>);}
Storing State in the URL
For certain types of state, such as filter or sort options in a list, storing state in the URL can be advantageous. This approach not only preserves state across page reloads but also enables sharing and bookmarking of specific application states.
With React Router, the useSearchParams
hook can be used to read and write query parameters, making it a suitable alternative to useState
for storing and retrieving UI state from the URL.
// Example of using useSearchParams for sorting statefunction ProductList () {const [searchParams, setSearchParams] = useSearchParams();const listData = [...FRUIT].sort((a, b) =>searchParams.get('sort') === 'ascending' ? a.localeCompare(b) : b.localeCompare(a));function handleSort(ascending = true) {setSearchParams({ sort: ascending ? 'ascending' : 'descending' });}// Render products based on the current sort parameter}
Persisting State with Local Storage
For long-term state persistence, such as user preferences, local storage can be a valuable tool. While this does not eliminate the need for useState
, it complements it by ensuring state persistence across sessions.
const isDarkModeStored = localStorage.getItem('isDarkMode');export default function DarkMode() {const [isDarkMode, setIsDarkMode] = useState(isDarkModeStored === 'true');function handleToggleMode() {setIsDarkMode((wasDarkMode) => !wasDarkMode);localStorage.setItem('isDarkMode', !isDarkMode);}// render UI}