Academind Logo

Avoid useState()

In some situations & use-cases, using useState() is not the optimal choice. There are preferrable alternatives!

Created by Maximilian Schwarzmüller

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 component
function 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 <input
type="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 form
function 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 submission
function 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 state
function 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
}

Recommended Courses