Machine Coding
Data Table
Master building a Data Table in React, covering column sorting, search filtering, pagination controls, and multi-row selection with checkboxes.
1. Learning Objectives
In this challenge, you will build a feature-rich Data Table component. By the end of this guide, you will be able to:
- Sort table columns in ascending and descending order by clicking column headers.
- Implement a global search filter that matches across multiple columns.
- Build client-side pagination with page navigation controls.
- Add multi-row selection using checkboxes with a "select all" toggle.
- Derive displayed data by chaining filter, sort, and slice operations.
2. Overview
A Data Table is one of the most commonly asked machine coding challenges. It tests your ability to compose multiple data transformations — filtering, sorting, and slicing — into a single rendering pipeline without mutating the original data source.
3. Why This Challenge Matters
Data tables teach fundamental data pipeline patterns:
- Composable Transformations: Chaining
.filter().sort().slice()mirrors real-world data pipeline patterns used in dashboards, admin panels, and reporting tools. - Derived State Mastery: The displayed rows are never stored in state; they are computed on every render from the raw data, search query, sort configuration, and current page.
4. Real-World Analogy
Think of a Data Table like a librarian organizing a card catalog:
- The Full Catalog: All card entries stored in a drawer — this is the raw data array in state.
- Search: Pulling out only cards that match a keyword — this is the
.filter()step. - Sort: Arranging the pulled cards alphabetically by title or by date — this is the
.sort()step. - Pagination: Placing only 10 cards on the display desk at a time — this is the
.slice()step.
5. Core Concepts
Below is a comparison of storing processed data in state versus computing it during render:
| Property | Storing Processed Data (Anti-pattern) | Computing During Render (Recommended) |
|---|---|---|
| State Variables | filteredData, sortedData, pagedData — three separate state variables. |
Only data, search, sortConfig, page in state. Derived values computed inline. |
| Synchronization Risk | High. Changing the search query requires updating all three derived states in the correct order. | None. The pipeline re-runs automatically when any input state changes. |
| Performance | Appears faster but risks stale data and bugs. | Use useMemo to cache expensive computations if needed. |
6. Syntax & API Reference
This example shows the data transformation pipeline — filter, sort, then paginate:
7. Visual Diagram
This diagram shows the data transformation pipeline:
8. Live Example — Full Working Code
Below is a complete HTML page demonstrating a data table with sort, search, pagination, and row selection:
What just happened? The raw USERS array is never mutated. The transformation pipeline — filter by search, sort by column, slice by page — runs inside useMemo on every render. Clicking a column header toggles its sort direction, the search input filters across all columns, and the pagination controls slice the results into pages.
9. Interactive Playground
Try It Yourself Challenges:
- Add a dropdown filter for the "Role" column that lets users filter by Admin, Editor, or Viewer.
- Add a "rows per page" selector that lets the user choose 5, 10, or 20 rows per page.
10. Common Mistakes
| Mistake | Why it happens | Wrong | Correct |
|---|---|---|---|
| Sorting the original data array | Using data.sort() mutates the original array in place, causing unpredictable renders and bugs when trying to "unsort". |
data.sort((a, b) => ...); |
[...data].sort((a, b) => ...); |
| Not resetting page on search | Typing a search query while on page 3 shows an empty page because the filtered result may have fewer than 3 pages. | setSearch(val); |
setSearch(val); |
11. Best Practices
- Never mutate the source array: Always spread or slice before sorting:
[...data].sort(). - Reset page on filter/sort changes: When the search query changes or the sort column changes, reset the current page to 1.
- Use
useMemofor expensive pipelines: Wrap the filter + sort pipeline inuseMemoto avoid recomputing on unrelated state changes. - Use
Setfor selections: ASetprovides O(1) lookup for.has(id), making it more efficient than arrays for selection tracking.
12. Browser Compatibility/Requirements
This project uses standard JavaScript array methods and runs on all modern browsers.
13. Interview Questions
Q1: Why should filter, sort, and paginate be derived computations rather than separate state variables?
Answer: Storing derived data in separate state variables creates synchronization risks. If the raw data, search query, or sort configuration changes, all derived states must be updated in the correct order. Computing them inline from the source data ensures they are always consistent and eliminates an entire class of bugs.
Q2: How would you handle server-side sorting and pagination instead of client-side?
Answer: Instead of processing data locally, send the sort column, sort direction, page number, and search query as query parameters to the API. The server returns only the relevant page of pre-sorted, pre-filtered results. State variables still track the same values, but they are used to construct API requests rather than to transform local data.
14. Debugging Exercise
Identify why clicking a column header to sort corrupts the original data and breaks the search filter:
Diagnosis: data.sort() mutates the original array in place. Since React compares by reference, it may not detect changes. The original ordering is permanently lost, so features like "unsort" or search (which depend on a stable source) break.
Fix: Create a copy before sorting:
15. Practice Exercises
Exercise 1: Inline cell editing
Make table cells editable. Double-clicking a cell should turn it into an input field. Pressing Enter saves the change and pressing Escape cancels it.
16. Scenario-Based Challenge
The server-side data table challenge:
Your table now has 100,000 rows. Client-side sorting and filtering is too slow. Redesign the component to delegate sorting, filtering, and pagination to a REST API. Describe what query parameters you would send, how you would handle loading states between page changes, and how you would debounce the search input to avoid excessive API calls.
17. Quick Quiz
Q1: Why must you call setPage(1) when the search query changes?
A) Because page numbers must always start at 1
B) Because the filtered result set may have fewer pages than the current page number
C) Because React requires sequential state updates
Answer: B — If you are on page 3 and the new search returns only 2 results, page 3 does not exist. Resetting to page 1 prevents showing an empty page.
18. Summary & Key Takeaways
- Build data tables using a composable pipeline:
filter → sort → slice. - Never mutate the source data array. Always spread before sorting.
- Reset the page number whenever the search query or sort configuration changes.
- Use
useMemoto cache expensive transformation pipelines. - Use a
Setfor efficient multi-row selection tracking.
19. Cheat Sheet
| Operation | Syntax Pattern |
|---|---|
| Safe sort | [...data].sort((a, b) => ...) |
| Global search | data.filter(r => Object.values(r).some(v => String(v).includes(q))) |
| Paginate | data.slice((page - 1) * perPage, page * perPage) |
| Toggle Set | next.has(id) ? next.delete(id) : next.add(id) |