React: Function Components and Props
Last updated: 2026-08-26
Function components are like Lego bricks—each brick has its own shape (props) and can be assembled and combined in various ways. Props are the "interfaces" of the bricks: whatever color or size you give the brick, that’s how it will look.
1. What You'll Learn
- Definition and Naming Conventions for Function Components
- Passing and Receiving Props
- Props types(string / number / booleano / objeto)
- children special prop
- Use TypeScript to annotate prop types
2. A True Story of a Front-End Team
Data Flow Between Components and Props
flowchart TD
A[Parent Component] -->|"name='Alice'"| B[UserCard]
A -->|"items=[...]"| C[ProductList]
C -->|"item={...}"| D[ProductCard]
B -->|"children"| E[<Avatar />]
A -->|"onFollow callback"| B
style A fill:#e3f2fd,stroke:#1565c0
style B fill:#e8f5e9,stroke:#2e7d32
style C fill:#fff3e0,stroke:#e65100
style D fill:#fce4ec,stroke:#c62828
(1) Pain Point: A button you have to copy and paste 50 times
Charlie is the technical lead of a front-end team. He discovered that there were more than 50 different buttons scattered across 20 pages in the code repository:
// Page A:Red Button
<button style={{ backgroundColor: 'red', color: 'white' }}>
Delete
</button>
// Page B:Blue Button
<button style={{ backgroundColor: 'blue', color: 'white' }}>
Submit
</button>
// Page C:Small gray button
<button style={{ backgroundColor: 'gray', color: 'black', fontSize: '12px' }}>
Cancel
</button>
Question:
- Design Changes: All buttons need to be rounded; this requires changes in 50 places.
- Inconsistency: Some buttons use
red, some use#ff0000, and some usergb(255,0,0) - Frequent bugs: Someone wrote the style incorrectly, causing the entire button to become transparent
(2) A Component-Based Approach
Unified management using function components and props:
function Button({ variant = 'primary', size = 'medium', children }) {
const styles = {
primary: { backgroundColor: '#1890ff', color: 'white' },
danger: { backgroundColor: '#ff4d4f', color: 'white' },
default: { backgroundColor: '#f0f0f0', color: '#333' }
}
return (
<button style={{
...styles[variant],
padding: size === 'large' ? '12px 24px' : '8px 16px',
border: 'none',
borderRadius: '6px',
cursor: 'pointer'
}}>
{children}
</button>
)
}
// Usage:Done in one line of code
<Button variant="danger">Delete</Button>
<Button variant="primary">Submit</Button>
<Button variant="default">Cancel</Button>
Benefits: The number of buttons has been reduced from 50 fragments to 1 component; design changes now require only one modification, ensuring a consistent style.
3. Fundamentals of Function Components
(1) Definition of a Component
In React, a function component is a JavaScript function that returns JSX.
// The Simplest Function Component
function Welcome() {
return <h1>Hello, React!</h1>
}
// How to Write Arrow Functions(More common)
const Welcome = () => {
return <h1>Hello, React!</h1>
}
// Implicit Return(Only one line JSX "when " can be omitted return)
const Welcome = () => <h1>Hello, React!</h1>
| Rule | Description |
|---|---|
| Capitalize the first letter | Welcome ✅ welcome ❌ (React distinguishes between components and HTML tags) |
| Return JSX | Must return a JSX element (or null) |
| Pure Functions | The same input (Props) must return the same output (UI) |
| Props Cannot Be Modified | Props are read-only and cannot be modified within the component |
(2) Props are function arguments
Props (short for "Properties") are read-only data passed from a parent component to a child component.
// ---- Define a Component ----
function Greeting(props) {
// props It is an object,Includes all incoming parameters
return <h1>Hello,{props.name}!This year, you {props.age} years old。</h1>
}
// ---- Using Components ----
<Greeting name="Alice" age={28} />
// Rendering Results:
// <h1>Hello,Alice!This year, you 28 years old。</h1>
(3) Destructuring Syntax for Props
A more common approach is deconstructive assignment, which directly retrieves the desired parameters:
// Deconstructive Writing Style(Recommendations)
function Greeting({ name, age }) {
return <h1>Hello,{name}!This year, you {age} years old。</h1>
}
// equivalent to:
function Greeting(props) {
const { name, age } = props
return <h1>Hello,{name}!This year, you {age} years old。</h1>
}
4. Types of Props
| Prop Type | Passing Syntax | Example of Receiving | Notes |
|---|---|---|---|
| String | name="Alice" |
{ name } → "Alice" |
Enclosed in double quotes |
| Number | age={30} |
{ age } → 30 |
Must use {} |
| Boolean | active / active={true} |
{ active } → true |
Omit value defaults to true |
| Object | info={{ key: 'val' }} |
{ info } → { key: 'val' } |
Double curly braces |
| Array | items={[1, 2, 3]} |
{ items } → [1,2,3] |
Must use {} |
| Function | onClick={handler} |
{ onClick } → fn |
Callback/Event Handler |
| Component | icon={<Icon />} |
{ icon } → JSX |
Dynamically Rendered Component |
Props can accept any JavaScript data type: strings, numbers, booleans, objects, arrays, functions, and even other components.
(1) String
Strings can be passed directly using double quotes (just like HTML attributes):
function UserCard({ name, role }) {
return (
<div className="card">
<h2>{name}</h2>
<p>Characters:{role}</p>
</div>
)
}
// Strings are enclosed in double quotes,For other types {} Package
<UserCard name="Alice" role="Administrator" />
(2) Numbers, Boolean Values, and Objects
Non-string types must be enclosed in {}:
function Product({ name, price, inStock, details }) {
return (
<div className="product">
<h3>{name}</h3>
<p>Price:${price}</p>
{/* Boolean values are used for conditional rendering */}
{inStock ? <span>In stock ✅</span> : <span>Out of stock ❌</span>}
{/* Object:Accessible properties */}
<p>Category:{details.category} | Weight:{details.weight}g</p>
</div>
)
}
// Using Components
<Product
name="Wireless Mouse"
price={89}
inStock={true}
details={{ category: 'Peripherals', weight: 120 }}
/>
(3) Quick Reference Table for Pass-by-Value Methods
| Type | Example | Description |
|---|---|---|
| String | name="Alice" |
Written the same way as HTML attributes |
| Number | count={42} |
Requires {} |
| Boolean value true | active or active={true} |
If only the property name is specified, the default is true |
| Boolean value false | active={false} |
Requires {} |
| Object | data={{ x:1, y:2 }} |
Double Braces |
| Array | items={[1,2,3]} |
Requires {} |
| Expression | result={a + b} |
Any JS expression |
| Variable | name={userName} |
Pass-by-value variable |
▶ Example: User Information Card
Output:
Form with input fields and submit handling
// ============================================
// Example:use Props Build a User Information Card
// Features:Display Props Transmission of Various Types
// ============================================
function UserCard({ name, age, isOnline, avatar, tags }) {
return (
<div style={{
border: '1px solid #ddd',
borderRadius: '8px',
padding: '20px',
maxWidth: '300px'
}}>
<img
src={avatar}
alt={name}
style={{ width: '80px', height: '80px', borderRadius: '50%' }}
/>
<h2>{name}</h2>
<p>Age:{age} years old</p>
<p>
Status:
<span style={{ color: isOnline ? '#52c41a' : '#999' }}>
{isOnline ? 'Online' : 'Offline'}
</span>
</p>
<div>
Tags:
{tags.map(tag => (
<span key={tag} style={{
background: '#f0f0f0',
padding: '2px 8px',
borderRadius: '4px',
margin: '0 4px',
fontSize: '12px'
}}>
{tag}
</span>
))}
</div>
</div>
)
}
// Using Components
<UserCard
name="Alice"
age={28}
isOnline={true}
avatar="https://i.pravatar.cc/80"
tags={['Front End', 'React', 'TypeScript']}
/>
Output:
User card: avatar, "Alice", "Age: 28 years old", status "Online" (green), tags: [Front End] [React] [TypeScript]
5. children and Default Values
(1) children: nested content within the component
children is a special Prop that represents the content between component tags.
// Definition:use children Content between tags
function Card({ children, title }) {
return (
<div style={{ border: '1px solid #ddd', padding: '16px', borderRadius: '8px' }}>
<h3>{title}</h3>
<div>{children}</div> {/* The content between the tags is displayed here */}
</div>
)
}
// Usage:The content between the tags automatically becomes children
<Card title="Announcement">
<p>The system will go live tonight 22:00 Perform maintenance,Expected to continue 2 hours。</p>
<p>Please save your work in advance.,To prevent data loss。</p>
</Card>
// Rendering Results:
// <div class="card">
// <h3>Announcement</h3>
// <div>
// <p>The system will go live tonight 22:00 Perform maintenance...</p>
// <p>Please save your work in advance....</p>
// </div>
// </div>
(2) Default Value
Use ES6 destructuring syntax with default values to set default values for props:
function Button({ text = 'Click', color = 'blue', size = 'medium' }) {
return (
<button style={{
backgroundColor: color,
padding: size === 'large' ? '12px 24px' : '8px 16px',
color: 'white',
border: 'none',
borderRadius: '4px'
}}>
{text}
</button>
)
}
// No parameters → Use the default values
<Button /> // Blue、medium、"Click"
// Pass some parameters → Override the default value
<Button text="Submit" /> // Blue、medium、"Submit"
<Button color="red" size="large" /> // Red、large、"Click"
6. TypeScript Type Annotations (Overview)
Using TypeScript, you can define strict types for props, enabling the editor to provide autocomplete suggestions and perform type checks:
// ---- Definition Props Type ----
interface ButtonProps {
text: string // Required:string
color?: string // Optional: add ? suffix
size?: 'small' | 'medium' | 'large' // Optional: limited to 3 values
onClick: () => void // Required:Function Types
disabled?: boolean // Optional: boolean, defaults to false
}
// ---- Using in a component ----
function Button({ text, color = 'blue', size = 'medium', onClick, disabled = false }: ButtonProps) {
return (
<button
onClick={onClick}
disabled={disabled}
style={{
backgroundColor: color,
padding: size === 'large' ? '12px 24px' : '8px 16px',
opacity: disabled ? 0.5 : 1,
color: 'white',
border: 'none',
borderRadius: '4px'
}}
>
{text}
</button>
)
}
// Usage:The editor provides smart suggestions
<Button
text="Save"
color="#1890ff"
size="medium"
onClick={() => alert('Saved successfully!')}
/>
// ❌ Error:TypeScript It will throw an error
<Button text="Test" /> // Required field is missing onClick
<Button text="Test" size="xl" onClick={fn} /> // size Outside the permitted range
7. Complete Example: Product Card Component
// ============================================
// Example:Product Card Component(Comprehensive Application Props Various Types)
// ============================================
// Type Definitions
interface Product {
id: number
name: string
price: number
image: string
category: string
tags: string[]
}
interface ProductCardProps {
product: Product
onAddToCart: (productId: number) => void
showDiscount?: boolean
size?: 'small' | 'large'
}
// Component Implementation
function ProductCard({
product,
onAddToCart,
showDiscount = false,
size = 'large'
}: ProductCardProps) {
return (
<div style={{
border: '1px solid #eee',
borderRadius: '8px',
padding: size === 'large' ? '16px' : '10px',
width: size === 'large' ? '280px' : '200px'
}}>
<img
src={product.image}
alt={product.name}
style={{ width: '100%', height: '150px', objectFit: 'cover' }}
/>
<h3>{product.name}</h3>
<p style={{ color: '#ff4d4f', fontSize: '20px', fontWeight: 'bold' }}>
${product.price}
{showDiscount && <span style={{ fontSize: '12px', color: '#999', textDecoration: 'line-through', marginLeft: '8px' }}>${(product.price * 1.2).toFixed(0)}</span>}
</p>
<p style={{ color: '#666', fontSize: '14px' }}>{product.category}</p>
<div>
{product.tags.map(tag => (
<span key={tag} style={{ background: '#f0f5ff', color: '#1890ff', padding: '2px 6px', borderRadius: '4px', margin: '0 2px', fontSize: '12px' }}>
{tag}
</span>
))}
</div>
<button
onClick={() => onAddToCart(product.id)}
style={{
width: '100%',
padding: '8px',
marginTop: '10px',
backgroundColor: '#1890ff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Add to Cart
</button>
</div>
)
}
// Using Components
const myProduct = {
id: 101,
name: 'Mechanical Keyboard',
price: 299,
image: 'https://via.placeholder.com/280x150',
category: 'Computer Peripherals',
tags: ['Hot Seller', 'New Products']
}
<ProductCard
product={myProduct}
onAddToCart={(id) => console.log('Add to Cart:', id)}
showDiscount={true}
/>
Expected Output: A product card displaying an image of a keyboard, the name "Mechanical Keyboard," the price $299 (strikethrough indicating the original price of $359), the category "Computer Peripherals," and the tags "Hot Seller" and "New Arrival," with a blue "Add to Cart" button below.
▶ Example 2: Comprehensive Use of Props Type Passing and Default Values
Output:
Subheading: "{name}". Displays: "Status:"
// ============================================
// Example:Profile Card——Props Combined Use of Types and Default Values
// Features:Display string、number、boolean、object、function etc. Props Type Passing
// ============================================
function ProfileCard({
name = 'Anonymous User',
age = 0,
isActive = false,
avatar = 'https://via.placeholder.com/60',
skills = [],
onGreet
}) {
return (
<div style={{
border: '1px solid #e8e8e8',
borderRadius: '12px',
padding: '20px',
maxWidth: '320px',
boxShadow: '0 2px 8px rgba(0,0,0,0.06)'
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: '16px', marginBottom: '16px' }}>
<img
src={avatar}
alt={name}
style={{ width: '60px', height: '60px', borderRadius: '50%', objectFit: 'cover' }}
/>
<div>
<h3 style={{ margin: 0 }}>{name}</h3>
<p style={{ margin: '4px 0 0 0', color: '#666', fontSize: '14px' }}>
{age} years old · {isActive ? '🟢 Online' : '🔴 Offline'}
</p>
</div>
</div>
{skills.length > 0 && (
<div style={{ marginBottom: '12px' }}>
<p style={{ margin: '0 0 8px 0', fontWeight: 'bold', fontSize: '14px' }}>Skills:</p>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '6px' }}>
{skills.map((skill, index) => (
<span key={index} style={{
backgroundColor: '#f0f5ff',
color: '#1890ff',
padding: '4px 10px',
borderRadius: '12px',
fontSize: '12px'
}}>
{skill}
</span>
))}
</div>
</div>
)}
{onGreet && (
<button
onClick={() => onGreet(name)}
style={{
width: '100%',
padding: '8px',
backgroundColor: '#1890ff',
color: 'white',
border: 'none',
borderRadius: '6px',
cursor: 'pointer',
fontSize: '14px'
}}
>
Say hello to {name}
</button>
)}
</div>
)
}
// Usage:Pass all parameters
<ProfileCard
name="Alice"
age={28}
isActive={true}
avatar="https://i.pravatar.cc/60"
skills={['React', 'TypeScript', 'Node.js']}
onGreet={(name) => alert(`Hello,${name}!`)}
/>
// Usage:Pass only some parameters(Use the default values for the rest)
<ProfileCard
name="Bob"
skills={['Vue', 'Python']}
/>
// Usage:Do not pass any parameters(Use all default values)
<ProfileCard />
Output:
Alice's profile: avatar, "28 years old · 🟢 Online", skills [React][TypeScript][Node.js], "Say hello to Alice" button → alert("Hello, Alice!")
▶ Example 3: Event Callback Props—Child Components Notifying Parent Components
Output:
Displays "products". state: adding, cart. buttons: {adding ? 'adding...' : `add ${productname}`}
function AddToCartButton({ productName, onAdd }) {
const [adding, setAdding] = useState(false)
async function handleClick() {
setAdding(true)
await onAdd(productName)
setAdding(false)
}
return (
<button onClick={handleClick} disabled={adding}
style={{ padding: '8px 16px', cursor: adding ? 'wait' : 'pointer' }}>
{adding ? 'Adding...' : `Add ${productName}`}
</button>
)
}
function ProductPage() {
const [cart, setCart] = useState([])
function handleAdd(name) {
setCart(prev => [...prev, name])
}
return (
<div>
<h2>Products</h2>
<AddToCartButton productName="Keyboard" onAdd={handleAdd} />
<AddToCartButton productName="Mouse" onAdd={handleAdd} />
<p>Cart: {cart.join(', ') || 'empty'}</p>
</div>
)
}
Output:
"Add Keyboard" button → "Adding..." then cart shows "Keyboard". "Add Mouse" button → cart: "Keyboard, Mouse". Empty cart shows "empty".
▶ Example 4: Conditional Rendering of Props—Dynamic Component Switching
Output:
Subheading: "Products". Displays: "Products". State: adding (setter: setAdding), cart (setter: setCart). Button: {adding ? 'Adding...' : `Add ${productName}`}. Async data fetching/loading states
function StatusMessage({ status }) {
const config = {
loading: { icon: '⏳', color: '#1890ff', text: 'Loading...' },
success: { icon: '✅', color: '#52c41a', text: 'Success!' },
error: { icon: '❌', color: '#ff4d4f', text: 'Error occurred' },
empty: { icon: '📭', color: '#999', text: 'No data' },
}
const { icon, color, text } = config[status] || config.empty
return (
<div style={{ padding: 16, textAlign: 'center', color }}>
<span style={{ fontSize: 32 }}>{icon}</span>
<p>{text}</p>
</div>
)
}
function DataViewer() {
const [status, setStatus] = useState('loading')
return (
<div style={{ maxWidth: 300, margin: '0 auto' }}>
<StatusMessage status={status} />
<div style={{ display: 'flex', gap: 4 }}>
{['loading', 'success', 'error', 'empty'].map(s => (
<button key={s} onClick={() => setStatus(s)}
style={{ padding: '4px 8px', cursor: 'pointer', fontSize: 12 }}>
{s}
</button>
))}
</div>
</div>
)
}
Output:
⏳ Loading... (blue) / ✅ Success! (green) / ❌ Error occurred (red) / 📭 No data (gray). Buttons switch status.
▶ Example 5: The renderProps Pattern—List Components and Custom Rendering
Output:
Displays "users"
function DataList({ items, renderItem, keyExtractor, emptyMessage = 'No items' }) {
if (!items || items.length === 0) {
return <p style={{ color: '#999', textAlign: 'center', padding: 20 }}>{emptyMessage}</p>
}
return (
<ul style={{ listStyle: 'none', padding: 0 }}>
{items.map(item => (
<li key={keyExtractor(item)} style={{ borderBottom: '1px solid #f0f0f0', padding: 8 }}>
{renderItem(item)}
</li>
))}
</ul>
)
}
function App() {
const users = [
{ id: 1, name: 'Alice', email: 'alice@example.com', active: true },
{ id: 2, name: 'Bob', email: 'bob@example.com', active: false },
{ id: 3, name: 'Charlie', email: 'charlie@example.com', active: true },
]
const products = [
{ id: 'a', name: 'Keyboard', price: 79 },
{ id: 'b', name: 'Monitor', price: 399 },
]
return (
<div style={{ maxWidth: 400, margin: '0 auto' }}>
<h3>Users</h3>
<DataList
items={users}
keyExtractor={u => u.id}
renderItem={u => (
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>{u.name} ({u.email})</span>
<span style={{ color: u.active ? '#52c41a' : '#999' }}>
{u.active ? 'Active' : 'Inactive'}
</span>
</div>
)}
/>
<h3>Products</h3>
<DataList
items={products}
keyExtractor={p => p.id}
emptyMessage="No products available"
renderItem={p => (
<span>{p.name} - ${p.price}</span>
)}
/>
</div>
)
}
Output:
Users: Alice (alice@example.com, Active), Bob (bob@example.com, Inactive), Charlie (charlie@example.com, Active)
Products: Keyboard - $79, Monitor - $399
❓ FAQ
<div> is an HTML div tag; <Div> is a custom component. If a component is written in lowercase, React will treat it as an HTML tag, causing the rendering to fail.function Card({ title = 'Untitled' }) {}; ② Use Card.defaultProps = { title: 'Untitled' }. Note that React no longer recommends using defaultProps (in function components); it is recommended to use destructuring to assign default values. In TypeScript, you can also use title?: string to indicate that a prop is optional, which makes the code clearer when combined with destructuring to assign default values.📖 Summary
- A function component is a JavaScript function that returns JSX; the first letter must be capitalized
- Props are read-only data passed from a parent component to a child component; the child component cannot modify them.
- Props can be of any type: string, number, boolean, object, array, or function
childrenis a special Prop that represents the content between component tags- Use destructuring syntax
function Comp({ prop1, prop2 })to make your code clearer - TypeScript type annotations can significantly improve the development experience and code robustness
📝 Exercises
- Basic Problem (Difficulty ⭐): Create a
Avatarcomponent that accepts two props—name(a string) andsize(a number, default 50)—and displays the user’s avatar and name. - Advanced Problem (Difficulty ⭐⭐): Create a
TagListcomponent that accepts two props—tags(an array of strings) andonRemove(a function)—and renders a list of tags. Clicking a tag deletes it. - Challenge (Difficulty ⭐⭐⭐): Create a
DataTablecomponent in TypeScript that acceptscolumns(an array of column definitions) anddata(an array of data) and renders a simple data table. Try adding astriped(Boolean, optional) prop to control the zebra pattern.