Sikau

React patterns at scale · Day 5 · about 45 min

Project: A headless data table

A hook with sorting, filtering and prop getters.

Three teams, three tables

Your company has an orders table, a staff table and a menu table. The design-system team built a <Table> component with sorting and filtering — and every team wanted different markup: cards on phones, sticky headers, a checkbox column. The component grew props until nobody dared change it. This is the moment teams discover the headless component: ship the behaviour as a hook and let each team render whatever it likes.

TanStack Table, Downshift and React Aria all work this way. Today you'll build a small version: useTable sorts, filters and gives you prop getters for your headers and buttons, while a presentational DataTable renders it. It pulls together everything in this unit — custom hooks, presentational components, derived state and inversion of control.

Behaviour in, markup out

The hook owns two pieces of state — the sort ({ key, dir } or null) and the filter text — and derives the visible rows from them with useMemo. It never renders a single element. Callers get back the rows plus functions that produce the right props for their own elements.

function useTable(rows, { columns, initialSort = null }) {
  const [sort, setSort] = useState(initialSort);
  const [filter, setFilter] = useState("");
  const visible = useMemo(() => {
    const q = filter.trim().toLowerCase();
    const kept = q ? rows.filter((r) => columns.some((c) => String(r[c.key]).toLowerCase().includes(q))) : rows;
    if (!sort) return kept;
    const sign = sort.dir === "asc" ? 1 : -1;
    return [...kept].sort((a, b) => sign * compare(a[sort.key], b[sort.key]));
  }, [rows, columns, sort, filter]);
  // …sortBy, getHeaderProps, getSortButtonProps
}

Compare numbers as numbers: sorting [180, 60, 250] as strings gives 180, 250, 60. Check typeof and fall back to localeCompare for text, which also handles accents and different alphabets correctly.

The rest of this lesson

  • Prop gettersexplain
  • String sortpredict
  • Clicking a columnorder
  • Who does whatmatch
  • Build useTablechallenge
  • Badge quizquiz
  • What you learnedrecap

Continue this lesson free — run real code in your browser

Continue with Google

See all 10 lessons of React patterns at scale