React Multiple State Variables
Table of Contents + −
In the last lesson you learned about React State with Arrays. Now let’s hold many separate pieces of state in one component, by calling useState again for each one.
🤔 Why do we need many state variables?
Think about a small sign-up form. It is never one value. It is a bunch of different values all sitting in the same component.
- A name field. A text value.
- An age field. A number.
- A checkbox like “subscribe to emails”. A true/false value.
These values don’t depend on each other, so squeezing them into a single value only makes your code harder.
- The name changing has nothing to do with the age changing, right?
- What you actually want is a separate little box for each one.
- That is exactly what multiple useState calls give you.
🧩 Each useState is fully independent
Here’s the key idea. You can call useState as many times as you like in one component, and each call creates its own separate state.
- Every
useStatereturns its own value and its own setter function. - They don’t share anything, so changing one does not touch the others.
- React keeps track of them by the order you call them, so you never mix them up.
- You read each value by its own name, then update it with its own setter.
Here is a component with three separate state variables. Notice each one has its own pair, a value and a setter.
import { useState } from "react";
function ProfileForm() { const [name, setName] = useState(""); const [age, setAge] = useState(0); const [isOpen, setIsOpen] = useState(false);
return <p>{name} is {age} years old.</p>;}Three independent boxes, three independent setters, and nothing leaks between them.
setNameonly changesname.setAgeonly changesage.setIsOpenonly changesisOpen.
Order matters
React matches each useState to its state by call order. So always call your hooks at the top of the component, in the same order every render. Never put a useState inside an if or a loop.
💡 A small profile form
Let’s wire those three values to real inputs so you can see them update live. Each input reads from one state variable and writes back with that variable’s setter.
import { useState } from "react";
function ProfileForm() { const [name, setName] = useState(""); const [age, setAge] = useState(0); const [isOpen, setIsOpen] = useState(false);
return ( <div> <input value={name} onChange={(e) => setName(e.target.value)} placeholder="Your name" /> <input type="number" value={age} onChange={(e) => setAge(Number(e.target.value))} /> <label> <input type="checkbox" checked={isOpen} onChange={(e) => setIsOpen(e.target.checked)} /> Subscribe to emails </label>
<p>{name || "No name"}, age {age}, subscribed: {isOpen ? "yes" : "no"}</p> </div> );}The thing to notice here is how clean each line is, since each input touches only its own state.
- Type in the name box, and only
nameupdates. - Change the number box, and only
ageupdates. - Tick the checkbox, and only
isOpenflips between true and false.
If a user types “Alex” and ticks the box, the bottom line reads like this.
Output
Alex, age 0, subscribed: yes🧱 When to group values into one object
So when should you NOT use separate variables? When the values truly belong together and always change as a set, a single object can be cleaner.
- Use separate
useStatecalls for values that change on their own. This is the common case. - Group values into one object only when they move together, like the x and y of a single mouse position.
- A good test is to ask, “do these always change at the same moment?” If yes, one object can fit. If no, keep them separate.
Here a mouse position is one idea made of two numbers, so a single object makes sense. Both numbers update in the same instant.
import { useState } from "react";
function MouseTracker() { const [position, setPosition] = useState({ x: 0, y: 0 });
function handleMove(e) { setPosition({ x: e.clientX, y: e.clientY }); }
return ( <div onMouseMove={handleMove}> Mouse is at {position.x}, {position.y} </div> );}Objects need a full copy
When you store an object in state, you must replace the whole object on update, and copy the old fields you want to keep with setUser({ ...user, age: 30 }). Forgetting the ...user spread will wipe out the other fields. Separate variables avoid this hassle, which is one more reason to prefer them for unrelated values.
⚠️ Don’t cram unrelated values into one object
A common beginner habit is to throw everything into one giant object because it “looks organized”. For values that have nothing to do with each other, that actually makes your code worse.
This is the mistake on the left and the cleaner choice on the right.
// ❌ Avoid - unrelated values jammed into one objectconst [form, setForm] = useState({ name: "", isModalOpen: false, theme: "light" });// Every update needs the spread, even to change one field:setForm({ ...form, name: "Alex" });
// ✅ Prefer - separate state for unrelated valuesconst [name, setName] = useState("");const [isModalOpen, setIsModalOpen] = useState(false);const [theme, setTheme] = useState("light");// Clean, direct updates:setName("Alex");See the difference?
- With separate variables, updating one value is a single clean call.
- With the giant object, every small change forces you to spread the whole thing.
- One missed
...quietly deletes the other fields, so separate state for unrelated values is the safer default.
✅ Best Practices
A few simple habits will keep your state easy to work with.
- Start with several simple
useStatecalls, one per independent value. This is the default for most components. - Group into an object only when the values truly belong together and always change at the same time.
- Always call your hooks at the top of the component, in the same order, never inside an
ifor a loop. - Name each setter to match its value, like
agewithsetAge. It keeps your code readable.
🧩 What You’ve Learned
- ✅ You can call
useStateas many times as you need, one per piece of state - ✅ Each
useStateis fully independent and has its own value and its own setter - ✅ Prefer several simple
useStatecalls for values that change on their own - ✅ Group values into one object only when they truly belong together and change as a set
- ✅ Objects in state need a full copy with the spread, so unrelated values are easier to keep separate
- ✅ Always call hooks at the top of the component, in the same order every render
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
How many times can you call useState in a single component?
Why: There is no limit. You call useState once for each independent piece of state, and each call gets its own value and setter.
- 2
What is true about two separate useState calls in the same component?
Why: Each useState call is completely separate. Its setter only changes its own value, and the calls never share data.
- 3
When is grouping values into one state object a good choice?
Why: Group into one object only when the values move together, like the x and y of a mouse position. Unrelated values are cleaner as separate variables.
- 4
Why are separate state variables often easier than one big object for unrelated values?
Why: With an object you must spread the old fields on every update or you lose them. Separate variables let you update one value with a single clean setter call.
🚀 What’s Next?
Now you can hold several pieces of state in one component and you know when to keep them apart. Next you’ll pull these habits together into a clear set of rules for managing state well.