Reputation: 590
I'm looking for some help to implement row select functionality in my React app.
I have the following data that I provide to the table(react-data-table-component):
const data = useMemo(
() =>
[...Array(87)].map((_, outer) => ({
id: `o-${outer}`,
name: randFullName(),
age: randNumber({ max: 100 }),
price: randNumber({ max: 5000 }),
description: randProductDescription(),
active: randBoolean(),
date: randFutureDate().toLocaleString(),
items: [...Array(randNumber({ max: 10 }))].map((__, inner) => ({
id: `o-${outer}--i-${inner}`,
name: randFullName(),
age: randNumber({ max: 100 }),
price: randNumber({ max: 5000 }),
description: randProductDescription(),
active: randBoolean(),
date: randFutureDate().toLocaleString(),
})),
})),
[],
);
I display this data in a table.
There is a parent row and each parent row can be expanded which contains the child rows.
Now, I want to be able to select rows. I use a three-state checkbox where three states are:
I want to render the checkbox states and also be able to access the selected rows.
I am using a custom selector cell like this:
const SelectorCell = ({
rowId,
parentId,
siblingIds,
childrenIds,
}: {
rowId: string;
parentId: string;
siblingIds: string[];
childrenIds: string[];
}) => {
const { toggleRow, rowSelectionStatus } = useContext(
NewTableSelectedRowsContext,
) as NewTableSelectedRowsType;
const handleToggle = useCallback(() => {
// todo
}, []);
const status = rowSelectionStatus(rowId);
return <ThreeStateCheckbox checked={status} onChange={handleToggle} />;
};
I have tried to achieve this using a context provider but haven't got any success:
import { createContext, useCallback, useMemo, useState } from "react";
import {
NewTableSelectedRowsType,
} from "./types";
const statusTypes = {
checked: true,
intermediate: null,
unchecked: false,
};
export const NewTableSelectedRowsContext = createContext<NewTableSelectedRowsType | null>(null);
interface Props {
children: JSX.Element;
}
export const NewTableSelectedRowsContextProvider = ({ children }: Props): JSX.Element => {
const rowSelectionStatus = useCallback((rowId: string) => // todo, []);
const toggleRow = useCallback(() => {
// todo
},
[],
);
const value: NewTableSelectedRowsType = useMemo(
() => ({
toggleRow,
rowSelectionStatus,
}),
[rowSelectionStatus, toggleRow],
);
return (
<NewTableSelectedRowsContext.Provider value={value}>
{children}
</NewTableSelectedRowsContext.Provider>
);
};
How can I achieve that in React?
Upvotes: 1
Views: 2803
Reputation: 2877
I'm not sure why you are using context to store the logic of selecting rows or not, you will need to send the data.length
to compute if the selected rows are the same amount as the table rows/part of absolutely 0.
You also haven't attached to component code, so my answer will be more general...
You can have state for table status and one for selected rows
enum ETableState {
checked = 'checked',
empty = 'empty',
intermediate = 'intermediate'
}
const [tableState, setTableState] = useState<ETableState>(ETableState.intermediate)
const [selectedRows, setSelectedRows] = useState<string[])([])
on each selecting row, you will call a callback for add/remove it from selectedRows
state, that after will trigger an effect
const toggleRow = useCallback((id: string) =>
setSelectedRows(prev =>
prev.includes(id) ? prev.filter(i => i != id) : [...prev, id]
)
, [selectedRows.length])
useEffect(() => {
setTableState(dataLength === selectedRows.length ?
ETableState.checked
: !selectedRows.length ?
ETableState.empty
: ETableState.intermediate
}
,[selectedRows.length, dataLength]
edit
In case you want to be able to use this logic in few separate places, as in child rows and so on, you can wrap all of this code in custom hook and call it in each component with the data.length
function useTableState(dataLength: number) {
...
return { selectedRows, tableState, toggleRow }
}
// usage in a table component
const { selectedRows, tableState, toggleRow } = useTableState(data.length)
Upvotes: 2