
ReactJS
π A dynamic list is one that changes during runtimeβitems are added, removed, or updated based on user actions or data changes. This is where proper key management becomes critical.
With dynamic lists, the order and content change constantly, making unique stable keys essential for maintaining correct component state.
interface Todo {
id: number;
text: string;
completed: boolean;
}
function TodoApp() {
const [todos, setTodos] = React.useState<Todo[]>([
{ id: 1, text: "Learn React", completed: false },
{ id: 2, text: "Build a project", completed: false }
]);
const [input, setInput] = React.useState("");
const addTodo = () => {
if (input.trim()) {
setTodos([
...todos,
{
id: Date.now(), // β
Unique ID for new item
text: input,
completed: false
}
]);
setInput("");
}
};
return (
<div>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === "Enter" && addTodo()}
placeholder="Add a new todo..."
/>
<button onClick={addTodo}>Add</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input type="checkbox" checked={todo.completed} />
{todo.text}
</li>
))}
</ul>
</div>
);
}function NotificationList() {
const [notifications, setNotifications] = React.useState<Notification[]>([]);
const addNotification = (message: string) => {
const newNotification: Notification = {
id: Date.now(),
message,
timestamp: new Date()
};
// Add to the beginning
setNotifications([newNotification, ...notifications]);
};
return (
<div>
<button onClick={() => addNotification("New notification!")}>
Add Notification
</button>
<ul>
{notifications.map(notif => (
<li key={notif.id}>
{notif.message} at {notif.timestamp.toLocaleTimeString()}
</li>
))}
</ul>
</div>
);
}
interface Notification {
id: number;
message: string;
timestamp: Date;
}interface Item {
id: number;
name: string;
}
function ItemList() {
const [items, setItems] = React.useState<Item[]>([
{ id: 1, name: "Item 1" },
{ id: 2, name: "Item 2" },
{ id: 3, name: "Item 3" }
]);
const removeItem = (id: number) => {
setItems(items.filter(item => item.id !== id));
};
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.name}
<button onClick={() => removeItem(item.id)}>Delete</button>
</li>
))}
</ul>
);
}function ShoppingList() {
const [items, setItems] = React.useState<string[]>(["Milk", "Bread", "Eggs"]);
const removeItem = (index: number) => {
setItems(items.filter((_, i) => i !== index));
};
return (
<ul>
{items.map((item, index) => (
<li key={`${item}-${index}`}>
{item}
<button onClick={() => removeItem(index)}>Remove</button>
</li>
))}
</ul>
);
}interface Post {
id: number;
title: string;
likes: number;
}
function PostList() {
const [posts, setPosts] = React.useState<Post[]>([
{ id: 1, title: "React Tips", likes: 10 },
{ id: 2, title: "TypeScript Guide", likes: 5 }
]);
const likePost = (id: number) => {
setPosts(posts.map(post =>
post.id === id ? { ...post, likes: post.likes + 1 } : post
));
};
return (
<ul>
{posts.map(post => (
<li key={post.id}>
<h3>{post.title}</h3>
<p>Likes: {post.likes}</p>
<button onClick={() => likePost(post.id)}>π Like</button>
</li>
))}
</ul>
);
}interface Todo {
id: number;
text: string;
completed: boolean;
createdAt: Date;
}
function ComplexTodoApp() {
const [todos, setTodos] = React.useState<Todo[]>([]);
const [input, setInput] = React.useState("");
const addTodo = () => {
if (input.trim()) {
setTodos([
...todos,
{
id: Date.now(),
text: input,
completed: false,
createdAt: new Date()
}
]);
setInput("");
}
};
const toggleTodo = (id: number) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
const deleteTodo = (id: number) => {
setTodos(todos.filter(todo => todo.id !== id));
};
const completedCount = todos.filter(t => t.completed).length;
const totalCount = todos.length;
return (
<div style={{ maxWidth: "500px", margin: "20px auto" }}>
<h2>My Todos</h2>
<p>Completed: {completedCount} of {totalCount}</p>
<div style={{ marginBottom: "20px" }}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === "Enter" && addTodo()}
placeholder="Add a new todo..."
style={{ padding: "8px", width: "70%" }}
/>
<button onClick={addTodo} style={{ marginLeft: "10px" }}>
Add
</button>
</div>
<ul style={{ listStyle: "none", padding: 0 }}>
{todos.map(todo => (
<li
key={todo.id}
style={{
padding: "10px",
border: "1px solid #ddd",
marginBottom: "10px",
borderRadius: "4px",
display: "flex",
alignItems: "center",
gap: "10px"
}}
>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span
style={{
flex: 1,
textDecoration: todo.completed ? "line-through" : "none",
color: todo.completed ? "#999" : "black"
}}
>
{todo.text}
</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
{todos.length === 0 && <p style={{ color: "#999" }}>No todos yet!</p>}
</div>
);
}<details><summary>π More Examples</summary>
interface Card {
id: number;
title: string;
}
function CardList() {
const [cards, setCards] = React.useState<Card[]>([
{ id: 1, title: "Card 1" },
{ id: 2, title: "Card 2" },
{ id: 3, title: "Card 3" }
]);
const insertCard = (title: string, position: number) => {
const newCard: Card = { id: Date.now(), title };
const newCards = [...cards];
newCards.splice(position, 0, newCard);
setCards(newCards);
};
return (
<div>
<button onClick={() => insertCard("New Card", 1)}>
Insert at Position 1
</button>
<ul>
{cards.map(card => (
<li key={card.id}>{card.title}</li>
))}
</ul>
</div>
);
}function BatchUpdateExample() {
const [items, setItems] = React.useState([
{ id: 1, name: "Item 1", selected: false },
{ id: 2, name: "Item 2", selected: false },
{ id: 3, name: "Item 3", selected: false }
]);
const selectAll = () => {
setItems(items.map(item => ({ ...item, selected: true })));
};
const deselectAll = () => {
setItems(items.map(item => ({ ...item, selected: false })));
};
return (
<div>
<button onClick={selectAll}>Select All</button>
<button onClick={deselectAll}>Deselect All</button>
<ul>
{items.map(item => (
<li key={item.id} style={{
backgroundColor: item.selected ? "#e3f2fd" : "white"
}}>
{item.name}
</li>
))}
</ul>
</div>
);
}</details>
| Operation | Method | Key Consideration |
|---|---|---|
| Add to end | `[...items, newItem]` | ID must be unique |
| Add to start | `[newItem, ...items]` | Use stable IDs, not index |
| Remove by ID | `.filter(item => item.id !== id)` | Stable keys unaffected |
| Update item | `.map(item => item.id === id ? {...item, prop} : item)` | Keep ID same |
| Batch update | `.map(item => {...item, prop})` | All IDs stable |
| Insert at position | `.splice(index, 0, item)` | Create new array copy first |
1. β Always use unique, stable IDs when adding dynamic items
2. β Create a new array when adding/removingβdon't mutate directly
3. β Use `.filter()` to remove items by ID
4. β Use `.map()` to update items while keeping others unchanged
5. β `Date.now()` works for generating unique IDs in simple apps
6. β Use `UUID` library for production apps to avoid ID collisions
7. β Batch operations with `.map()` are efficient
8. β Always create new array references for React to detect changes
9. β Dynamic lists benefit most from proper key management
10. β Test list operations with reordering, filtering, and batch changes
You've mastered the basics of lists and keys! Now explore advanced patterns and optimizations.
Challenge: Build a fully functional todo app
Resources
Ojasa Mirai
Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.
Learn Deep β’ Build Real β’ Verify Skills β’ Launch Forward