Hey Jane,
Great questions! Hooks can be a bit tricky at first, but they're incredibly powerful once you get them down.
useState vs. Global State Management
For form inputs, useState is generally your go-to for local component state. It's simple and efficient for managing the state of a single form or a few related input fields within a component.
You'd typically reach for global state management solutions like Redux or Zustand when:
- You need to share state across many components that are not directly related (i.e., not parent-child).
- The state is complex and has many interconnected parts.
- You need features like time-travel debugging or advanced middleware.
Performance-wise, useState itself is quite performant. The potential performance issues with forms often arise from how you structure your state updates and re-renders. If you have a very large form with many fields, consider breaking it down into smaller components or using techniques like memoization if you notice performance degradations.
useEffect and Dependency Arrays
The dependency array in useEffect is crucial. Here's a good rule of thumb:
- Include all values from the component scope (props and state) that the effect uses.
- If the effect doesn't use any values from the component scope, provide an empty array
[] to run it only once after the initial render (similar to componentDidMount).
- If the effect depends on a value that changes on every render (like a newly created function), consider wrapping that value in
useCallback or moving it outside the component if it's static.
Here's a simple example of fetching data:
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
setUser(data);
setLoading(false);
})
.catch(error => {
console.error("Error fetching user:", error);
setLoading(false);
});
}, [userId]); // Re-run effect only when userId changes
if (loading) {
return <p>Loading user...</p>;
}
return (
<div>
<h2>{user.name}</h2>
<p>Email: {user.email}</p>
</div>
);
}
Notice how userId is in the dependency array. If userId changes, the effect will re-run to fetch the new user's data. If it's empty [], it would only fetch on the initial mount.
Hope this helps!