I use React Aria Components' Table with context to select or deselect all the rows in the table.
However, I get Property 'selectedKeys' does not exist on type 'WithRef<TableProps, HTMLTableElement> | SlottedValue<WithRef<TableProps, HTMLTableElement>>'.
Property 'selectedKeys' does not exist on type 'SlottedValue<WithRef<TableProps, HTMLTableElement>>'.
with this code:
import { useContext } from "react";
import {
TableContext,
Table,
TableHeader,
TableBody,
Row,
Cell,
Column,
} from "react-aria-components";
import { Checkbox } from "./Checkbox";
type User = {
firstName: string;
lastName: string;
id: number;
};
export default function App() {
const users: User[] = [
{ firstName: "John", lastName: "Doe", id: 1 },
{ firstName: "Jane", lastName: "Doe", id: 2 },
{ firstName: "Joe", lastName: "Doe", id: 3 },
];
const tableContext = useContext(TableContext);
const selectedKeysCount = tableContext?.selectedKeys.size || 0;
const handleSelectionChange = () => {
if (selectedKeysCount === users.length) {
tableContext?.onSelectionChange(new Set());
return;
}
const newSelectedKeys = new Set();
users.forEach((user) => newSelectedKeys.add(user.id));
tableContext?.onSelectionChange(newSelectedKeys);
};
return (
<>
<Checkbox
slot="selection"
isIndeterminate={
selectedKeysCount > 0 && selectedKeysCount < users.length
}
isSelected={selectedKeysCount > 0 || false}
onChange={handleSelectionChange}
>
Select all
</Checkbox>
<Table aria-label="Users" selectionMode="multiple">
<TableHeader>
<Column isRowHeader />
<Column>First Name</Column>
<Column>Last Name</Column>
</TableHeader>
<TableBody items={users}>
{(item) => (
<Row>
<Cell>
<Checkbox slot="selection" />
</Cell>
<Cell>{item.firstName}</Cell>
<Cell>{item.lastName}</Cell>
</Row>
)}
</TableBody>
</Table>
</>
);
}
Here's a minimal reproduction sandbox.
My current solution is to sort of ignore the problem, but I'd like to understand why the type isn't inferred correctly and how I can tell typescript which type to use.
export default function App() {
// ...
const tableContext = useContext(TableContext);
// @ts-expect-error TypeScript doesn't recognise `selectedKeys` on `tableContext`
const selectedKeysCount = tableContext?.selectedKeys.size || 0;
const handleSelectionChange = () => {
if (selectedKeysCount === users.length) {
// @ts-expect-error TypeScript doesn't recognise `onSelectionChange` on `tableContext`
tableContext?.onSelectionChange(new Set());
return;
}
const newSelectedKeys = new Set();
users.forEach((user) => newSelectedKeys.add(user.id));
// @ts-expect-error TypeScript doesn't recognise `onSelectionChange` on `tableContext`
tableContext?.onSelectionChange(newSelectedKeys);
};
// ...
}
Got it to work by using RAC's useSlottedContext
instead of useContext
:
import {
// ...
useSlottedContext,
} from "react-aria-components";
export default function App() {
// ...
const tableContext = useSlottedContext(TableContext);
const selectedKeysCount = tableContext?.selectedKeys.size || 0;
const handleSelectionChange = () => {
if (selectedKeysCount === users.length) {
tableContext?.onSelectionChange(new Set());
return;
}
const newSelectedKeys = new Set();
users.forEach((user) => newSelectedKeys.add(user.id));
tableContext?.onSelectionChange(newSelectedKeys);
};
// ...
}