Capítulo 25 de 54
Registered createTableHook components (table.PaginationControls, cell.TextCell, header.SortIndicator) read their instance from React context via useTableContext()/useCellContext()/useHeaderContext() rather than through props — but most cell/header components don't even need that: cell.table, header.table, row.table already link back, so context is really for components with no instance to start from (a standalone toolbar or pagination control).
<table.AppTable> provides the table instance; <table.AppCell cell={cell}> provides the cell instance; <table.AppHeader header={h}> / <table.AppFooter header={h}> provide the header instance. Everything registered under tableComponents/cellComponents/headerComponents reads back via the matching hook.useTableContext() at all. cell.table, header.table, row.table are already on the instance you have — e.g. call cell.table.nextPage() directly from inside a cell component instead of reaching for context. Reserve useTableContext() for components genuinely disconnected from any instance.createTableHook call site, not from the package directly — that's the only way they come back typed with your TFeatures and your registered component maps (so table.PaginationControls/cell.TextCell type-check).createTableHookContexts for prop-drilling avoidance, independent of createTableHook: column/row/cell/header instances are stable references, so putting one on a context and reading it deep in a subtree is safe — the reference doesn't change on state updates, so context consumers won't spuriously re-render from state changes alone.header.column.getIsSorted(), cell.row.getIsSelected(), cell.getValue()) will not re-render when that state changes, because the instance reference itself didn't change. Wrap those specific reads in Subscribe/useSelector, exactly as in the React Compiler Guide's nested-component pattern — it's the same underlying issue.table is the one exception — never put it on your own context as-is. useTable's React-facing return value gets a fresh reference whenever its selected state changes (that's intentional, so compiler-memoized JSX invalidates correctly) — so it is explicitly not stable, and providing it through your own createContext re-renders every consumer whenever the providing component re-renders. If you need a stable handle to pass down, provide table.store or a specific atom (table.atoms.rowSelection) instead, and read with Subscribe/useSelector, or keep the core table in a ref.createTableHookContexts) only matter when one table setup's provider is nested inside another's, where a consumer could otherwise read the wrong (nearest) provider. Contexts from createTableHookContexts are typed with TFeatures only (not your component maps) — prefer the hooks from your actual createTableHook call for full typing; use the createTableHookContexts hooks only from a module that can't import the createTableHook result.// no context needed — the instance you already have links to its table
function TextCell() {
const cell = useCellContext<string>()
return <span>{cell.getValue()}</span>
}
function PaginationControls() {
const table = useTableContext() // genuinely disconnected — needs context
return <button onClick={() => table.nextPage()}>Next</button>
}
// stable prop-drilling escape hatch, unrelated to createTableHook
const { cellContext, useCellContext } = createTableHookContexts<typeof features>()
useTableContext() earning its keep only for components with no instance already in hand; createTableHookContexts as a general stable-value context helper.cell.table/row.table/header.table before reaching for useTableContext() — it's usually unnecessary.Subscribe/useSelector around it, or it silently goes stale — this is the same trap as the React Compiler Guide's nested-component case, just via context instead of props.useTable return value on your own context — provide table.store or a specific atom instead if you need a stable handle.table.store/table.atoms as the actually-stable alternative to the raw table object.