How to initially auto-expand all rows with react-table 7?

Viewed 2545

I'm looking for an easy and clean way to make all rows to be auto-expanded when using react-table v7.

When I map all the data to true and set it as an initial state, it does not expand my rows.

const expanded = {
  1: true,
  2: true,
  ...
};

...

useTable(
  {
    ...
    initialState: {
      expanded
    }
  }
);

By dumping table state expanded prop, I've found out there are generated non-numeric keys like {Col1:somevalue2: true} (grouped columns). I don't have an access to those keys before table instance is created and therefore I also don't have an access to row ids in order to generate initial state.

The only way I've found is below but I'd prefer the tableInstance to be expanded initially (as a result of useTable() hook).

React.useEffect(() => tableInstance.toggleAllRowsExpanded(true), []);

Relevant Github FR discussion: https://github.com/tannerlinsley/react-table/discussions/3317

2 Answers

Like you mentioned, using toggleAllRowsExpanded(true) is going to be the easiest way to expand all of the rows. However, if you want this to happen within useTable, you can write a custom plugin hook which runs this logic in the useInstance hook based on a new initialState.startExpanded boolean.

// useStartExpanded.tsx
import { useEffect } from 'react'
import { Hooks, TableInstance } from 'react-table'

export interface UseStartExpandedState<D extends object> {
  startExpanded: boolean
}

export const useStartExpanded = <D extends object = {}>(hooks: Hooks<D>): void => {
  hooks.useInstance.push(useInstance)
}
useStartExpanded.pluginName = 'useStartExpanded'

const useInstance = <D extends object = {}>(instance: TableInstance<D>): void => {
  const {
    state: { startExpanded },
    toggleAllRowsExpanded
  } = instance

  useEffect(() => {
    if (startExpanded) toggleAllRowsExpanded(true)
  }, [startExpanded, toggleAllRowsExpanded])
}

Usage would look something like this:

// MyTable.tsx
import { useMemo } from 'react'
import { Column, useExpanded, useGroupBy, useTable } from 'react-table'
import { useStartExpanded } from './useStartExpanded'

export type Data = { id: string }

export const MyTable = ({ startExpanded = true }) => {
  const data: Data[] = useMemo(() => [], [])
  const columns: Column<Data>[] = useMemo(() => [], [])

  const tableInstance = useTable<Data>(
    { columns, data, initialState: { startExpanded } },
    useGroupBy,
    useExpanded,
    useStartExpanded
  )

  return <>...</>
}

Since this example is in TypeScript, you'll need to add the startExpanded to the TableState type definition.

// react-table-config.d.ts
import { UseExpandedState, UseGroupByState } from 'react-table'
import { UseStartExpandedState } from './useStartExpanded'

declare module 'react-table' {
  export interface TableState<D extends object = {}>
    extends UseExpandedState<D>,
      UseGroupByState<D>,
      UseStartExpandedState<D> {}
}

What you wrote makes intuitive sense based off of what the documentation says, but is missing a key element. You have to tell initialState that your "expanded" object is tied to the "expanded" property, like this:

  const initialExpanded = React.useMemo(() => {
    return {
      "expanded": {
        "0": true,
        "4": true
      }
  }}, [])
  const { /* ... */ } = useTable({
    initialState: { initialExpanded }, 
    /* etc */
  });
Related