Components and JSX
7 pages · ~4 min✓ Reviewed
Start here. Next up: State and Props.
Components and JSX Mental Model
1. What a Component Is
- A function component takes props, returns JSX
- Name starts with a capital, or
<card />is an HTML tag - UI = f(props, state): body is pure, no side effects
2. JSX Is Objects
- JSX is sugar for
jsx()calls returning plain element objects, not DOM nodes
jsx
const el = <h1 className="t">Hi</h1>; // compiles to const el = jsx("h1", { className: "t", children: "Hi" });
el is a plain object, not a DOM node
3. Props and Renders
- Props or state change
- Function re-called
- New element tree
- Diff, patch DOM
- Each render is a fresh snapshot
- Props are read-only: never mutate, pass new values down
Remember
Render only computes a description. React diffs it and touches the DOM only where it changed.
JSX Rules and Core Workflow
1. JSX Rules
- One root: wrap siblings in a parent or a fragment
<>...</> - Close every tag:
<img />,<input /> {}embeds expressions (variable, call, ternary), notiforfor
| HTML | JSX |
|---|---|
class | className |
for | htmlFor |
onclick, tabindex | onClick, tabIndex |
style="color:red" | style={{ color: 'red' }} (object) |
2. Static vs Dynamic
| Written | Kind | Passes |
|---|---|---|
name="Ada" | static | a string |
size={40} | dynamic | a number |
{user.name} | dynamic | any expression |
3. Core Workflow
- Definefunction Name
- Destructureprops in signature
- Return JSXone root
- Render<Name prop={value} />
jsx
function Avatar({ size, name }) { return <img width={size} alt={name} />; } // <Avatar size={40} name="Ada" />
Expressions only
No if or for inside {}: use a ternary or .map().
Lists, Keys and Conditional Rendering
1. Lists and Keys
- key goes on the outermost element returned inside
map - Keys are stable and unique among siblings
- Use a database id, not
Math.random() - Index key + reorder, insert or delete: state lands on the wrong row, because React matches by key
jsx
items.map(item => (
<Row key={item.id} {...item} />
))stable id as key
2. Conditional Rendering
| Pattern | Result |
|---|---|
c ? <A /> : <B /> | picks one of two |
c && <A /> | A only when true |
return null | renders nothing |
{count && <Badge />}renders 0 when count is 0- Fix:
{count > 0 && <Badge />}
3. Early Return and Reset
- Early return guards first, so the main JSX stays flat
jsx
if (!user) return <Login />; return <Dashboard user={user} />;
Key resets state
Change a component's key and React remounts it, so its state resets: <Editor key={id} />
Composition, Children and Fragments
1. What children Is
children= special prop holding JSX nested between the tags- Composition beats inheritance: generic wrapper, content passed in
jsx
function Card({ children }) { return <div className="card">{children}</div>; } <Card>content</Card>
2. Named Slots
- Slots are plain props that accept JSX
- Nested content still arrives as
children
jsx
<Layout header={<Nav />} sidebar={<Menu />}>
main
</Layout>3. Fragments
| Syntax | Use |
|---|---|
<>...</> | Groups siblings, no DOM node, no key |
<Fragment key={id}> | Needs a key in a list |
4. Lifting Content Up
Pass it through children
Avoids prop drilling. Passed-in elements are not re-rendered when the wrapper's state changes.
Common Mistakes
1. Component Mistakes
| Mistake | Fix | Why it breaks |
|---|---|---|
| Component defined inside another | Move it to module level | New type each render: remount, state lost |
<card /> | <Card /> | Lowercase is an HTML element |
Card(props) | <Card {...props} /> | Calling it directly breaks hooks |
2. Lists and Render Mutation
- Missing
key, or key on the inner element: warning and bad diffing - Key goes on the element map returns
items.sort()in render mutates the array; copy first
jsx
// wrong: key on the inner element items.map(i => <li><b key={i.id}>{i.name}</b></li>) // right: key on the returned element items.map(i => <li key={i.id}>{i.name}</li>) // copy, then sort [...items].sort(byName).map(/* ... */)
3. Interview Point
- Why does an inner component lose state? New type every render, so React remounts it
- Why not call
Card(props)? No element, so hooks run in the parent
Remember
Capitalised, top-level, used as <Card />, keyed on the returned element, and copy before sort
Cheat Sheet
1. Component and JSX
- Capital name, destructure props, return one root
- Close every tag:
<img />,<input /> className,htmlFor;{expr}for JS;style={{ color: 'red' }}
jsx
function Card({ title, children }) { return <div className="card">{children}</div>; }
2. Lists and conditionals
keygoes on the outer element inmap: a stable unique id, not the index
jsx
items.map(x => <Item key={x.id} {...x} />)| Need | Write |
|---|---|
| Either/or | ok ? <A /> : <B /> |
| Show/hide | ok && <A /> (boolean only) |
| Nothing | return null |
| Count check | n > 0 && <A /> |
3. Composition
children:<Wrapper>content</Wrapper>- Slot props:
<Layout header={<Nav />}>main</Layout> - Fragment
<>...</>: siblings, no wrapper node
The 0 trap
{count && <Badge />} renders 0 when count is 0.
Check yourself
count is 0. What does {count && <Badge />} put on the page?
- It renders the number 0
&&returns the falsy left side, and React prints numbers- Fix:
count > 0 && <Badge />
Bug hunt: items.map(i => <li><Row key={i.id} /></li>) warns about keys. Why?
- The key is on the inner
Row, not on thelithat map returns - Move it:
<li key={i.id}>
Rows use key={index} and each holds a text input. You delete the first row, A. Whose typed text does the first row now show?
- A's text, now beside B's label: key 0 survives and keeps its state
- Use
key={item.id}so A's state leaves with A