SyntaxStudy
Sign Up
React Dynamic Lists with State: Add, Remove, Reorder
React Beginner 1 min read

Dynamic Lists with State: Add, Remove, Reorder

A static list rendered from a constant array is straightforward, but real applications need lists that change at runtime. Combining `useState` (which holds the array) with `map()`, `filter()`, and the spread operator (for immutable updates) gives you everything needed to add, remove, update, and reorder list items reactively. Every state update produces a new array reference that triggers a re-render, which React reconciles efficiently using the `key` prop. Adding an item means returning a new array with the new item appended: `[...prev, newItem]`. Removing an item uses `filter`: `prev.filter(item => item.id !== id)`. Updating a single item uses `map`: `prev.map(item => item.id === id ? { ...item, ...changes } : item)`. Reordering is more involved and typically uses a drag-and-drop library, but manual reordering can be done by swapping two indices using `splice` on a copy. When the list data comes from a server, it is fetched in a `useEffect` and stored in state alongside loading and error state. The combination of `useState`, `useEffect`, and conditional rendering around a `.map()` is the most common pattern in React applications and covers the vast majority of real-world data display requirements.
Example
import { useState } from 'react';

let nextId = 4;

function TaskManager() {
  const [tasks, setTasks] = useState([
    { id: 1, text: 'Design wireframes', done: false },
    { id: 2, text: 'Write tests',       done: true  },
    { id: 3, text: 'Deploy to staging', done: false },
  ]);
  const [input, setInput] = useState('');

  // Add
  const addTask = () => {
    if (!input.trim()) return;
    setTasks(prev => [...prev, { id: nextId++, text: input.trim(), done: false }]);
    setInput('');
  };

  // Toggle done
  const toggle = (id) =>
    setTasks(prev => prev.map(t => t.id === id ? { ...t, done: !t.done } : t));

  // Remove
  const remove = (id) =>
    setTasks(prev => prev.filter(t => t.id !== id));

  const pending   = tasks.filter(t => !t.done);
  const completed = tasks.filter(t =>  t.done);

  return (
    <div style={{ maxWidth: 400 }}>
      <div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>
        <input
          value={input}
          onChange={e => setInput(e.target.value)}
          onKeyDown={e => e.key === 'Enter' && addTask()}
          placeholder="New task…"
          style={{ flex: 1 }}
        />
        <button onClick={addTask}>Add</button>
      </div>

      <h4>Pending ({pending.length})</h4>
      <ul>
        {pending.map(t => (
          <li key={t.id}>
            <button onClick={() => toggle(t.id)}>✓</button>
            {' '}{t.text}{' '}
            <button onClick={() => remove(t.id)}>✕</button>
          </li>
        ))}
      </ul>

      <h4>Done ({completed.length})</h4>
      <ul style={{ color: '#888' }}>
        {completed.map(t => (
          <li key={t.id} style={{ textDecoration: 'line-through' }}>
            {t.text}{' '}
            <button onClick={() => remove(t.id)}>✕</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default TaskManager;