Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Race condition between the groupBy plugin and svelte rerender #209

Open
lolcabanon opened this issue Mar 22, 2024 · 1 comment
Open

Race condition between the groupBy plugin and svelte rerender #209

lolcabanon opened this issue Mar 22, 2024 · 1 comment

Comments

@lolcabanon
Copy link
Contributor

There seems to be somewhat of a race condition (not sure it's the right term, but it illustrates the problem) between the groupBy plugin and svelte rerender (because of the {#each} block maybe?).

Here's the actual problem :

When I trigger a group operation (hCellProps.group.toggle()) on some columns (not all, seemingly1), some $originalRows are displayed in place of the $rows (or $pageRows) that should show.

Notes and observations :

  • The same originalRows are showing each time (in my case, rows 5, 6, 8, 9, 10)
  • The grouping occurs correctly if the same column key is set in initialGroupByIds
  • Then if I ungroup / regroup, chaos starts in the table
  • But the $pageRows store is dumped with correct columns (see <pre> tags in the bottom)

A peek of my current code : (I'll try to put a reproduction somewhere when I have more time)

<script>
// ...

const table = createTable(readable(data), {
  select: TablePlugins.addSelectedRows(),

  columnFilter: TablePlugins.addColumnFilters(),
  tableFilter: TablePlugins.addTableFilter({
    fn: ({ filterValue, value }) => {
      return value.toLowerCase().includes(filterValue.toLowerCase());
    }
  }),

  sort: TablePlugins.addSortBy({
    isMultiSortEvent: isShiftClick,
    initialSortKeys: [{ id: 'createdAt', order: 'desc' }]
  }),

  group: TablePlugins.addGroupBy({
    // disableMultiGroup: true
    isMultiGroupEvent: isShiftClick
    // initialGroupByIds: ['statutActuelStr']
  }),

  expanded: TablePlugins.addExpandedRows({}),

  resize: TablePlugins.addResizedColumns(),
  grid: TablePlugins.addGridLayout(),
  pagination: TablePlugins.addPagination({
    initialPageSize: 100
  }),
});

let columns = table.createColumns({
  table.display({  // this column shows expand button if rows are grouped
    id: 'expand',
    header: (headerCell, { pluginStates }) =>
      createRender(RowsUngrouper, {
        groupByIds: pluginStates.group.groupByIds
      }),
    cell: ({ row }, { pluginStates }) => {
      const { canExpand, isExpanded } =
        pluginStates.expanded.getRowState(row);
      return createRender(RowExpander, { isExpanded, canExpand });
    },
    plugins: {
      resize: {
        disable: true,
        initialWidth: CELL_WIDTH.MIN
      },
    }
  }),

  // ...

  table.column({  // this column is the one I group by
    header: 'Statut',
    accessor: 'statutActuelStr',
    cell: statutCell,
    plugins: {
      group: {
        getGroupOn: (v) => (v === null ? 'Indéterminé' : v)
      },
      columnFilter: txtColFilter,
      resize: {
        initialWidth: CELL_WIDTH.LARGE,
        minWidth: CELL_WIDTH.MED
      }
    }
  }),

  // ...
});

const tableViewModel = table.createViewModel(columns, {
  rowDataId: (item, _index) => item.id.toString()
});
</script>

<table>
  <thead>
    ...
  </thead>
  <tbody>
  {#each $pageRow as pageRow, i (pageRow.id)}
    <Subscribe
      pageRowAttrs={pageRow.attrs()}
      let:pageRowAttrs
      pageRowProps={pageRow.props()}
      let:pageRowProps
    >
      <tr {...pageRowAttrs} data-row-index={i} on:click={() => rowClicked(pageRow)}>
        {#each pageRow.cells as cell (cell.id)}
          <TBodyCell {cell} />
          {/each}
        </tr>
      </Subscribe>
  {/each}
  </tbody>
</table>

<!-- for debugging : here rows seems to be correctly shown after a rerender -->
<section>
  <details>
    <summary>$originalRows</summary>
    <pre>{JSON.stringify(
      $originalRows.map(
        (r, i) =>
          `(${String(i).padStart(3, ' ')}) id : ${r.id.padStart(6, ' ')}, nom : ${r.cells.find((c) => c.id == 'prenom')?.value ?? 'Inconnu'}`
          ),
          getCircularReplacer(),
          2
      )}</pre>
  </details>
  <details>
    <summary>$rows</summary>
    <pre>{JSON.stringify(
      $rows.map(
        (r, i) =>
          `(${String(i).padStart(3, ' ')}) id : ${r.id.padStart(6, ' ')}, nom : ${r.cells.find((c) => c.id == 'prenom')?.value ?? 'Inconnu'}`
          ),
          getCircularReplacer(),
          2
      )}</pre>
  </details>
  <details>
    <summary>$pageRows</summary>
    <pre>{JSON.stringify(
      $pageRows.map(
        (r, i) =>
          `(${String(i).padStart(3, ' ')}) id : ${r.id.padStart(6, ' ')}, nom : ${r.cells.find((c) => c.id == 'prenom')?.value ?? 'Inconnu'}, cells : 
          ${r.cells.map((c) => c.value).join()}`
        ),
        getCircularReplacer(),
        2
      )}</pre>
  </details>
</section>

Dirty fix :

<script>
const getRows = async (current) => await new Promise((res) => setTimeout(() => res(current), 0));
</script>

<tbody {...$tableBodyAttrs}>
  {#await getRows($pageRows)}
    Loading...
  {:then finalRows}
    {#each finalRows as row, i (row.id)}
      ...
    {/each}
  {/await}
<tbody/>

Versions :

{
  "@sveltejs/kit": "^2.5.4",
  "svelte": "^4.2.12",
  "svelte-headless-table": "^0.18.2"
}

Screenshots :

image

image

image

Footnotes

  1. When there are around 10 groups or more, it tends to happen frequently.

@lolcabanon
Copy link
Contributor Author

I'm thinking maybe I should use svelte tick function to solve this? Not sure how it works...

https://learn.svelte.dev/tutorial/tick

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant