Lists, keys, forms, the boring stuff that breaks production
Three things trip up every React beginner: rendering lists, picking a key, and getting a form to actually accept input. All three are easy once you see what React is doing.
Lists are just .map
To render an array of things, you .map it to an array of JSX.
function RestaurantList({ items }) {
return (
<ul>
{items.map(r => (
<li key={r.id}>{r.name}</li>
))}
</ul>
);
}That is the entire pattern. No for loop, no template directive. JavaScript you already know.
The key prop, explained
React reuses DOM nodes between renders to be fast. To do that, it needs to know which item in the new list matches which item in the old list. The key is that identity.
- Use a stable, unique id. Database id, UUID, slug. Whatever does not change when the list reorders.
- Keys only need to be unique among siblings, not globally.
- Never index-as-key for dynamic lists.
Why index-as-key bites
Say you have ['Biryani', 'Dosa'] with keys 0, 1. User adds 'Idli' at the top. New list: ['Idli', 'Biryani', 'Dosa'] with keys 0, 1, 2. React thinks key 0 is the same item, so it keeps the 'Biryani' DOM node, just changes the text to 'Idli'. Any input state, focus, or animation inside that row is now on the wrong item.
The Swiggy reorder bug I spent a Saturday on: checkbox state stuck to the wrong row after delete. The "fix" was deleting
key={index}and usingkey={item.id}.
For static lists that never reorder, index is fine. When in doubt, give every item a real id.
Controlled inputs (the React way)
In React, the input's value lives in state. You read it from state, write back via onChange. Single source of truth.
function SearchBar() {
const [q, setQ] = useState('');
return (
<input
value={q}
onChange={e => setQ(e.target.value)}
placeholder="Search Bengaluru"
/>
);
}This is why beginners think the form "swallows" input. If you write <input value={q} /> but forget onChange, React keeps forcing the value back to q, which never updates. The input looks frozen. Add onChange, life resumes.
Uncontrolled inputs
Sometimes you do not need state on every keystroke; just the value on submit. Use a ref.
const ref = useRef();
return (
<form onSubmit={e => {
e.preventDefault();
console.log(ref.current.value);
}}>
<input ref={ref} defaultValue="" />
<button>Go</button>
</form>
);Smaller bundle, less re-rendering, but you lose live validation and conditional UI. Pick controlled for anything fancy, uncontrolled for plain submit-once forms.
Quick rules
- Always
e.preventDefault()on form submit, otherwise the page reloads. onChangefires on every keystroke in React, not just on blur. Yes, different from vanilla HTML.- Forms with many fields? Use
react-hook-form. It is the answer for anything past three inputs.
Next: lifting state and Context, for when components need to talk to each other.
Free tools you can use while you learn
Watching quietly. Tap me if you want a tip.
Try this (0 of 1 done)
- 1
Add a fourth item "Filter Coffee" to the list.
show answer
function App() { const items = ['Idli', 'Vada', 'Dosa', 'Filter Coffee']; return <ul>{items.map((it) => (<li key={it}>{it}</li>))}</ul>; }