DEV Community

Cover image for 🎯 Day 3 — Converting My Split Bill Form Into a Fully Controlled React Form (Hands-On Learning)
Usama
Usama

Posted on

🎯 Day 3 — Converting My Split Bill Form Into a Fully Controlled React Form (Hands-On Learning)

Today’s React progress was all about something fundamental but extremely important:
turning my FormSplitBill into a fully controlled form.

This sounds simple…
But once you start working with multiple inputs, derived values, and validation, you realize:

Controlled forms are not just about managing input.
They shape your app’s entire data-flow.

So here’s what I learned, how I refactored the form, and why it's a big milestone in my React journey.


🧩 What I Started With: (Partially Controlled Form)

Earlier, my form was somewhere between controlled and uncontrolled.

Some fields depended on React state, while others depended directly on the DOM.
For example, the "Friend’s Expense" field was calculated, but I wasn’t consistently handling input changes.

It worked…
but it wasn’t reliable.


🔥 Step 1 — Create State for Every Input

A controlled form means every input gets its value from state, and every change goes into state.

Here’s the core setup:

const [bill, setBill] = useState("");
const [yourExp, setYourExp] = useState("");
const paidByFriend = bill ? bill - yourExp : "";
const [whoIsPaying, setWhoIsPaying] = useState("user");
Enter fullscreen mode Exit fullscreen mode

This immediately gives you:

  • Single source of truth
  • Predictable UI
  • Easy debugging

🔥 Step 2 — Make Every Input Controlled

Before, my inputs were behaving independently.
Now each one has:

  1. A value controlled by state
  2. An onChange that updates state

Example:
Bill input → full controlled version:

<input
  type="text"
  value={bill}
  onChange={(e) => setBill(Number(e.target.value))}
/>
Enter fullscreen mode Exit fullscreen mode

User expense input:

<input
  type="text"
  value={yourExp}
  onChange={(e) =>
    setYourExp(
      Number(e.target.value) > bill ? yourExp : Number(e.target.value)
    )
  }
/>
Enter fullscreen mode Exit fullscreen mode

Notice the second one contains validation logic — something uncontrolled forms make painful.


🌟 Step 3 — Derived Values Become Easy

Because everything is in React state, calculating values becomes clean:

const paidByFriend = bill ? bill - yourExp : "";
Enter fullscreen mode Exit fullscreen mode

No querying DOM.
No refs.
No document.getElementById() nonsense.

Just pure React logic.


🔥 Step 4 — Make the Select Box Controlled Too

This was my favorite part.

Before:

  • The dropdown worked
  • But it didn’t store who was paying consistently

Now it’s clean:

<select
  value={whoIsPaying}
  onChange={(e) => setWhoIsPaying(e.target.value)}
>
  <option value="user">You</option>
  <option value="friend">{selectedFriend.name}</option>
</select>
Enter fullscreen mode Exit fullscreen mode

One controlled select = predictable business logic.


🧠 What I Learned Today (Big Mindset Shift)

Converting the form to a fully controlled component forced me to understand:

✔ React state should always drive UI
✔ Inputs shouldn’t store their own values
✔ Derived values become cleaner when state is centralized
✔ Validation becomes easier
✔ Debugging becomes 10× simpler

Most importantly, I learned this:

A form isn’t just UI — it’s business logic.
And React controlled components give you full control over it.


🛠️ Next Step (Tomorrow’s Work)

➡ Connect form output to friend balance updates
➡ Add app-level state lifting
➡ Clean up small bugs in Add Friend form
➡ Improve UX with disable/enable logic

Top comments (0)