Can anyone help me resolve the typing issues below for a FluentUI details list?
I have the following code to display a list of items using Fluent UI details list. I need to add the functionality to sort items in each column in ascending order like this:
const buildColumns = (): IColumn[] => {
const columns: IColumn[] = [];
columns.push({
key: "name",
name: "Name",
fieldName: "name",
minWidth: 75,
maxWidth: 75,
isResizable: false,
onColumnClick: _onColumnClick
});
columns.push({
key: "age",
name: "Age",
fieldName: "age",
minWidth: 200,
maxWidth: 200,
isResizable: false,
onColumnClick: _onColumnClick
});
return columns;
}
function _copyAndSort<T>(items: T[], columnKey: string, isSortedDescending?: boolean): T[] {
const key = columnKey as keyof T;
return items.slice(0).sort((a: T, b: T) => ((isSortedDescending ? a[key] < b[key] : a[key] > b[key]) ? 1 : -1));
}
const _onColumnClick = (ev: React.MouseEvent<HTMLElement>, column: IColumn): void => {
const [columns, setColumns] = useState<[]>();
const [names, setNames] = useState<[]>();
const newColumns: IColumn[] = columns.slice(); // error: columns' is possibly 'undefined'.
const currColumn: IColumn = newColumns.filter(currCol => column.key === currCol.key)[0];
newColumns.forEach((newCol: IColumn) => {
if (newCol === currColumn) {
currColumn.isSorted = true;
currColumn.isSortedDescending = !currColumn.isSortedDescending;
} else {
newCol.isSorted = false;
newCol.isSortedDescending = true;
}
});
const newItems = _copyAndSort(names, currColumn.fieldName!, currColumn.isSortedDescending); // error: Argument of type '[] | undefined' is not assignable to parameter of type 'never[]'.
Type 'undefined' is not assignable to type 'never[]'.
setColumns(newColumns); // error: Argument of type 'never[]' is not assignable to parameter of type 'SetStateAction<[] | undefined>'.
Type 'never[]' is not assignable to type '[]'.
Target allows only 0 element(s) but source may have more.
setNames(newItems); // error: Argument of type 'IColumn[]' is not assignable to parameter of type 'SetStateAction<[] | undefined>'.
Type 'IColumn[]' is not assignable to type '[]'.
Target allows only 0 element(s) but source may have more.
}
export const NamesListBase: React.FunctionComponent<INamesListProps> = (
props: INamesListProps
) => {
var columns = buildColumns();
const names = useSelector(selectAllNames)
return (
<div>
<DetailsList
items={names}
columns={columns}
/>
</div>
);
}
Any pointers on how to fix these typing errors in the code?
There's a few things that you can do to get this working. Here's a functional code sandbox to review: https://codesandbox.io/s/cocky-morning-8hqowd?file=/src/App.tsx
In short:
onColumnHeaderClick
, update the sorting state to be whatever column was clicked. If it's the same column twice, you could reverse the direction which is a nice UX.The key is that sorting is state and, your list of items and columns need to be derived from that state in order for the interaction to work.
Hope that helps!
export default function App() {
const [sortKey, setSortKey] = useState<string | undefined>(undefined);
const [isSortedDescending, setIsSortedDescending] = useState(false);
const items = [
{ age: 30, name: "Bob" },
{ age: 31, name: "Alice" }
];
const columns: IColumn[] = [
{
key: "name",
fieldName: "name",
name: "Name",
minWidth: 100,
isSorted: sortKey === "name",
isSortedDescending
},
{
key: "age",
fieldName: "age",
name: "Age",
minWidth: 100,
isSorted: sortKey === "age",
isSortedDescending
}
];
const sortedItems = [...items].sort((a, b) => {
if (sortKey === "age") {
return isSortedDescending ? b.age - a.age : a.age - b.age;
}
if (sortKey === "name") {
return isSortedDescending
? b.name.localeCompare(a.name)
: a.name.localeCompare(b.name);
}
return 0;
});
function onColumnHeaderClick(
event?: MouseEvent<HTMLElement>,
column?: IColumn
) {
if (column) {
setIsSortedDescending(!!column.isSorted && !column.isSortedDescending);
setSortKey(column.key);
}
}
return (
<DetailsList
onColumnHeaderClick={onColumnHeaderClick}
columns={columns}
items={sortedItems}
/>
);
}