-
Notifications
You must be signed in to change notification settings - Fork 6
Table
Use the Table
component to quickly display information about an array of objects into a table (such as the ones returned by the query functions). You can also configure it to support the sorting of the rows.
On its simplest form, Table
only requires a data
prop containing an array of objects as well as a columns
prop, an array of objects specifying the columns to be used and how to render the cells in that column.
An item in columns
may have the following keys:
-
key
(required): a uniquekey
among the columns. -
header
(required): something to display on the first row of the table. -
render
: when rendering the cell located in the ith row and the current column,render
is passed the ith item indata
. Ifrender
is not specified,data[i][key]
will be used to render instead. -
sorting
: aSorting
parameter. See the next section. -
onChangeSorting
: a callback when the sorting changes. See the next section. -
headerClassName
: given to theth
element. -
cellClassName
: given to everytd
element.
Suppose we have a User
interface:
interface User {
name: string;
age: number;
}
The following creates a two-column table where the first column displays the name of the person and the second column displays “Minor” or “Adult” depending on their age:
// Fetch it somehow.
users: User[] = ...
columns = [
{
key: "name",
header: "Name",
},
{
key: "type",
header: "Type",
render: (user) => user.age < 18 ? "Minor" : "Adult",
}
];
return <Table data={users} columns={columns}/>;
Each column in a Table
may be in three states: unsorted, in ascending order or in descending order. These three states are represented by the Sorting
enum (respectively Unsorted
, Ascending
and Descending
.
As stated before, the columns
items accept a sorting
key which is undefined
by default. Providing a state holding a Sorting
value will cause an arrow to appear on the right of the header. Furthermore, the callback provided to onChangeSorting
will be called when clicking on these arrows. It is thus possible to update the state given to sorting
and it would look a bit like this.
// Fetch it somehow.
users: User[] = ...
const [nameSorting, setNameSorting] = useState(Sorting.Unsorted);
columns = [
{
key: "name",
header: "Name",
sorting: nameSorting,
onChangeSorting: (newSorting) => setNameSorting(nameSorting),
},
{
key: "type",
header: "Type",
render: (user) => user.age < 18 ? "Minor" : "Adult",
}
];
return <Table data={users} columns={columns}/>;
Please note that Table
does not modify your data, even when sorting it. In our example, you still have to use nameSorting
wherever you fetch your data to change users
accordingly.
Having to keep track of the Sorting
states manually is a bit cumbersome. This is why useColumns
was invented. This hook takes an array of objects which keys are the same as Column
, except that instead of specifying sorting
and onChangeSorting
, you just specify a boolean canSort
. If true
, the hook will generate the corresponding states and callbacks. It will then return a Column[]
object ready to be injected into the columns
prop of Table
. So the above example is equivalent to:
// Fetch it somehow.
users: User[] = ...
const { columns, sorting } = useColumns([
{
key: "name",
header: "Name",
canSort: true,
},
{
key: "type",
header: "Type",
render: (user) => user.age < 18 ? "Minor" : "Adult",
}
]);
return <Table data={users} columns={columns}/>;
Note that you also get a sorting
object containing the Sorting
states, which are meant to be used in the part of your code which fetches user
. The next section describes how to use it with the classic REST API.
Django REST Framework supports the ordering of the results (see OrderingFilter
). Let's suppose that we fetch our User[]
with an URL like /api/v1/users/?ordering=[param]
where param is -name
or name
.
If using useBetterQuery
, the code certainly looks something like this (we assume that api.users.list
knows how to deal with keys which are compatible with toUrlParams
).
const { data: users, error, status } = useBetterQuery(
["api.users.list", { get_parameter: some_value }],
api.users.list
)
(...)
To integrate the sorting, just transform the sortings
object with sortingToApiParameter
:
const { columns, sorting } = useColumns([ ... ])
const { data: users, error, status } = useBetterQuery(
[
"api.users.list",
{
get_parameter: some_value,
ordering: sortingToApiParameter(sorting),
}
],
api.users.list
)
(...)
And that's it! Now, every time you click on the header of the table, the states inside sorting
are updated and the query is refetched.
What is inside our sorting
object exactly? If the object we pass to useColumns
is the following:
[
{
key: "name",
header: "Name",
canSort: true,
},
{
key: "type",
header: "Type",
render: (user) => user.age < 18 ? "Minor" : "Adult",
}
]
then sorting
will have one key, name
, containing Sorting.Unsorted
, Sorting.Ascending
or Sorting.Descending
. sortingToApiParameters
will then transform this object into respectively undefined
, name
or -name
(this is the very parameter which will be sent to the backend with the ordering
key).
What if, for some reason, the GET parameter should not be name
but something else like first_name
or user__name
? One solution would be to change the key in useColumns
, but this is not always practical if you use the key to specify the property to render. However, you can also specify a mapping
object to sortingToApiParameters
:
sortingToApiParameter(
sorting,
{
name: "user__name",
}
)
Now, the GET request will be sent to /api/v1/users/?ordering=user__name
.
- Multi-sorting is not supported by
useColumns
.
This is a living document, YOU should feel responsible for updating it.
- Code workflow
- Tests
- Project management (issues, PR, reviews)