Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
Keys help React identify which items in a list have changed, been added, or removed. They give items a stable identity across renders!
Add and remove items! Keys remain stable
function TodoList() {
const [todos, setTodos] = React.useState([
{ id: 'todo-1', text: 'Learn React', completed: false },
{ id: 'todo-2', text: 'Master Keys', completed: false },
{ id: 'todo-3', text: 'Build Project', completed: false }
]);
const [input, setInput] = React.useState('');
const addTodo = () => {
if (input.trim()) {
const newTodo = {
id: 'todo-' + Date.now(),
text: input,
completed: false
};
setTodos([...todos, newTodo]);
setInput('');
}
};
const removeTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};
return (
<div className="todo-app">
<h2>✅ Todo List (Good Keys)</h2>
<p className="subtitle">Each item has unique, stable ID</p>
<div className="add-section">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && addTodo()}
placeholder="Add new todo..."
/>
<button onClick={addTodo} className="add-btn">Add</button>
</div>
<ul className="todo-list">
{todos.map(todo => (
<li key={todo.id} className="todo-item">
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span className={todo.completed ? 'completed' : ''}>
{todo.text}
</span>
<button onClick={() => removeTodo(todo.id)} className="delete-btn">
❌
</button>
<code className="key-display">key: {todo.id}</code>
</li>
))}
</ul>
<div className="info">
<p>💡 Try adding/removing items - keys stay stable!</p>
<p>Total items: {todos.length}</p>
</div>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<TodoList />);Loading preview...
Why array index causes problems
// ❌ BAD: Using array index
function TodoList() {
const [todos, setTodos] = useState([
'Learn React',
'Build project',
'Deploy app'
]);
const removeTodo = (index) => {
setTodos(todos.filter((_, i) => i !== index));
};
return (
<ul>
{todos.map((todo, index) => (
<li key={index}> {/* ❌ Index causes bugs! */}
{todo}
<button onClick={() => removeTodo(index)}>Delete</button>
</li>
))}
</ul>
);
}
// Problem: When you delete item 1:
// - Item 2 becomes index 1 (was index 2)
// - Item 3 becomes index 2 (was index 3)
// React thinks items moved, causes re-renders and bugs!How React tracks changes with keys
// Without keys, React can't track items
// Initial render: ['A', 'B', 'C']
// After adding 'D' at start: ['D', 'A', 'B', 'C']
// ❌ Without keys:
// React thinks: "Change A to D, B to A, C to B, add C"
// Destroys and recreates all components!
// ✅ With keys:
// React thinks: "Add D, keep A, B, C"
// Only creates one new component!
function ItemList({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}> {/* React tracks by ID */}
<input defaultValue={item.name} />
</li>
))}
</ul>
);
}
// Without keys:
// - All inputs get recreated
// - User input is lost!
// With keys:
// - Only new item is created
// - Existing inputs preserved!Best practices for ID generation
// Generate unique IDs for items
import { v4 as uuidv4 } from 'uuid';
function AddTodo() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
const addTodo = () => {
setTodos([
...todos,
{
id: uuidv4(), // ✅ Unique ID
text: input,
completed: false
}
]);
setInput('');
};
return (
<div>
<input
value={input}
onChange={e => setInput(e.target.value)}
/>
<button onClick={addTodo}>Add</button>
<ul>
{todos.map(todo => (
<li key={todo.id}> {/* ✅ Stable unique key */}
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
{todo.text}
</li>
))}
</ul>
</div>
);
}
// Alternative: Use Date.now() + Math.random()
// id: Date.now() + '-' + Math.random()Essential rules for React keys
// KEY RULES FOR REACT KEYS
// ✅ Rule 1: Keys must be UNIQUE among siblings
const list1 = [
<Item key="a" />,
<Item key="b" />,
<Item key="c" /> // ✅ All different
];
// ❌ DON'T: Duplicate keys
const list2 = [
<Item key="a" />,
<Item key="a" />, // ❌ Duplicate!
<Item key="b" />
];
// ✅ Rule 2: Keys must be STABLE (not change)
// Good: Database ID, UUID
const goodKey = item.id; // ✅ Never changes
// Bad: Random number, array index when reordering
const badKey = Math.random(); // ❌ Changes every render!
// ✅ Rule 3: Keys don't need to be globally unique
function App() {
return (
<>
<List1>
<Item key="a" /> {/* OK */}
</List1>
<List2>
<Item key="a" /> {/* OK - different parent */}
</List2>
</>
);
}
// ✅ Rule 4: Don't use keys for anything else
// Keys are NOT passed as props!
function Item({ key }) {
console.log(key); // ❌ undefined!
}
// If you need the value, pass it separately:
<Item key={item.id} id={item.id} />Specific scenarios where index is acceptable
// When is using INDEX as key acceptable?
// ✅ OK: Static list (never changes)
const items = ['Apple', 'Banana', 'Orange'];
items.map((item, index) => (
<li key={index}>{item}</li> // OK - list is static
));
// ✅ OK: Items never reordered, added, or removed
const months = ['Jan', 'Feb', 'Mar', 'Apr']; // Fixed list
months.map((month, index) => (
<option key={index}>{month}</option> // OK
));
// ❌ NOT OK: Dynamic list
// - Items can be added/removed
// - Items can be reordered
// - Items have form inputs
// - Items have local state
// Use this rule:
// If your list can change → DON'T use index
// If your list is static forever → index is OKAlways use unique, stable identifiers from your data as keys
Don't use array index for dynamic lists - it causes bugs
Proper keys enable React to optimize rendering efficiently
If data lacks IDs, generate UUIDs when creating items