React
Lists and Keys
Build dynamic lists of transactions and KPI cards for DataFlow using React's key prop system
Working with lists in React feels different from vanilla JavaScript. Instead of manipulating the DOM directly, you map over data and let React handle the updates. The key prop tells React which items changed, moved, or were removed.
Think of keys like IDs on database records. React uses them to track each list item across re-renders. Without proper keys, React has to guess what changed.
Rendering Basic Lists
The DataFlow team needs to display transaction data in their dashboard. Here's how to render an array of items:
function TransactionList() {
const transactions = [
{ id: 1, company: 'Stripe', amount: 2400 },
{ id: 2, company: 'PayPal', amount: 1800 },
{ id: 3, company: 'Square', amount: 3200 }
];
return (
<div>
{transactions.map(transaction => (
<div key={transaction.id}>
{transaction.company}: ${transaction.amount}
</div>
))}
</div>
);
}What just happened?
React called map() on the array and returned JSX for each item. The key prop helps React track which transaction is which. Try this: open dev tools and watch React optimize updates when data changes.
Why Keys Matter
Keys aren't just a React requirement — they're performance optimization. When you add an item to the beginning of a list without proper keys, React re-renders every single item.
Common Mistake
Never use array index as key: key={index}. When items get reordered, React loses track of which component represents which data. Use unique, stable IDs instead.
Here's what happens with and without proper keys when the DataFlow team sorts their transaction table:
function SortableTransactions() {
const [transactions, setTransactions] = useState([
{ id: 101, company: 'Stripe', amount: 2400 },
{ id: 102, company: 'PayPal', amount: 1800 },
{ id: 103, company: 'Square', amount: 3200 }
]);
const sortByAmount = () => {
const sorted = [...transactions].sort((a, b) => b.amount - a.amount);
setTransactions(sorted);
};
return (
<div>
<button onClick={sortByAmount}>Sort by Amount</button>
{transactions.map(tx => (
<div key={tx.id}>{tx.company}: ${tx.amount}</div>
))}
</div>
);
}What just happened?
React used the unique id keys to track which transaction moved where during sorting. No DOM nodes were destroyed and recreated — just moved. Try this: click sort multiple times and notice how smooth it feels.
Complex List Components
Real dashboard lists need more than just text. The DataFlow team wants rich transaction cards with actions. Here's how to build reusable list items:
function TransactionCard({ transaction, onDelete }) {
return (
<div style={{ padding: '16px', border: '1px solid #e2e8f0', borderRadius: '8px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<h3>{transaction.company}</h3>
<button onClick={() => onDelete(transaction.id)}>Delete</button>
</div>
<p>Amount: ${transaction.amount}</p>
<p>Status: {transaction.status}</p>
</div>
);
}
function TransactionDashboard() {
const [transactions, setTransactions] = useState([
{ id: 1, company: 'Stripe', amount: 2400, status: 'completed' },
{ id: 2, company: 'PayPal', amount: 1800, status: 'pending' }
]);
const deleteTransaction = (id) => {
setTransactions(prev => prev.filter(tx => tx.id !== id));
};
return (
<div>
{transactions.map(transaction => (
<TransactionCard
key={transaction.id}
transaction={transaction}
onDelete={deleteTransaction}
/>
))}
</div>
);
}What just happened?
Each TransactionCard gets its own key and handles its own rendering. When you delete one, React removes only that specific component from the virtual DOM. Try this: delete all transactions and see the empty state message appear.
Keys with Dynamic Data
Dashboard data changes constantly. The DataFlow team receives live transaction updates. Here's how to handle dynamic lists that grow and shrink:
function LiveTransactions() {
const [transactions, setTransactions] = useState([]);
const [nextId, setNextId] = useState(1);
const addRandomTransaction = () => {
const companies = ['Stripe', 'PayPal', 'Square', 'Braintree'];
const randomCompany = companies[Math.floor(Math.random() * companies.length)];
const randomAmount = Math.floor(Math.random() * 5000) + 100;
const newTransaction = {
id: nextId,
company: randomCompany,
amount: randomAmount,
timestamp: new Date().toLocaleTimeString()
};
setTransactions(prev => [newTransaction, ...prev]);
setNextId(prev => prev + 1);
};
return (
<div>
<button onClick={addRandomTransaction}>Add Transaction</button>
<p>Total: {transactions.length} transactions</p>
{transactions.map(tx => (
<div key={tx.id}>
{tx.timestamp} - {tx.company}: ${tx.amount}
</div>
))}
</div>
);
}What just happened?
React efficiently updates the list as new items appear. Each transaction keeps its unique ID even when the array changes. The counter increments ensure no key collisions. Try this: add several transactions quickly and watch how React smoothly animates new items in.
Nested Lists and Fragment Keys
Sometimes DataFlow needs grouped data — transactions by month, KPIs by category. Nested lists require keys at every level:
function GroupedTransactions() {
const transactionsByMonth = {
'January': [
{ id: 1, company: 'Stripe', amount: 2400 },
{ id: 2, company: 'PayPal', amount: 1800 }
],
'February': [
{ id: 3, company: 'Square', amount: 3200 },
{ id: 4, company: 'Braintree', amount: 1500 }
]
};
return (
<div>
{Object.entries(transactionsByMonth).map(([month, transactions]) => (
<div key={month}>
<h3>{month}</h3>
{transactions.map(tx => (
<div key={tx.id}>
{tx.company}: ${tx.amount}
</div>
))}
</div>
))}
</div>
);
}What just happened?
React rendered two separate lists — one for months and one for transactions within each month. Each level has its own keys: months use their name, transactions use their ID. Try this: imagine adding a new month or transaction and how React would update just that section.
Pro Tip
When mapping over objects, Object.entries() gives you both key and value. Perfect for creating lists from grouped data like this DataFlow monthly breakdown.
Performance Considerations
Large lists can slow down your dashboard. React provides several optimization strategies. The most important: stable keys and avoiding inline functions in props.
Here's what not to do when rendering 1000+ DataFlow transactions:
Performance Killer
This creates new functions on every render:
{transactions.map(tx => <div key={tx.id} onClick={() => handle(tx.id)}>)}
Instead, use useCallback or move handlers outside the render loop. For massive lists, consider virtualization libraries that only render visible items.
But for most DataFlow use cases — hundreds of transactions, not thousands — React's default behavior works perfectly. Profile first, optimize later.
Quiz
1. DataFlow displays 50 transactions that users can sort by date or amount. What should you use as the key prop?
2. What happens when you use array index as key and then sort the DataFlow transaction list?
3. Which is the correct way to render a list of DataFlow transaction components?
Up Next: Forms in React
Build DataFlow's transaction entry form with controlled inputs, validation, and form submission handling.