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

[bug]: View Options do Not Persist when Search Params Change #893

Closed
dcyoung opened this issue Feb 20, 2025 · 1 comment
Closed

[bug]: View Options do Not Persist when Search Params Change #893

dcyoung opened this issue Feb 20, 2025 · 1 comment

Comments

@dcyoung
Copy link

dcyoung commented Feb 20, 2025

Expected Behavior

User's selection of viewed columns should persist when data updates.

  1. Toggle visibility of various columns
  2. Change filters OR pagination params
  3. WHILE data for current params is loading, user should see a DataTableSkeleton
  4. AFTER data for current params has loaded, user should see the DataTable with the same columns toggled during step 1

Current Behavior

When using @sadmann7 's recommended approach to indicate loading, the View options are reset whenever params change and new data is done loading.

How to reproduce

  1. indicate loading state using suspense key
<React.Suspense
    key={`${JSON.stringify(search)}`}
    fallback={
      <DataTableSkeleton ... />
    }
>
    <MyTable promise={promise} />
</React.Suspense>
  1. from UI, toggle column visibility to a non-default state
  2. update search params (ex: change page)

This resets any "view"/visibility options to their default state.

@dcyoung dcyoung changed the title [bug]: View Options do Not Persist when search params changes [bug]: View Options do Not Persist when Search Params Change Feb 20, 2025
@dcyoung
Copy link
Author

dcyoung commented Mar 4, 2025

I ended up putting the user's "View Selection" in the URL state, so it will trigger suspense boundary similar to other search params. This works ok given the # of columns is finite - so the URL can't grow too large. I combat ugly urls by only storing values as they differ from a "default" set provided to the main table hook.

The custom parser that serializes/deserializes non-default view state to the URL

const VisibilityStateSchema = z.record(z.string(), z.boolean());
function findInADiffThanB(
  recordA: Record<string, boolean>,
  recordB: Record<string, boolean>,
): Record<string, boolean> {
  const difference: Record<string, boolean> = {};

  for (const [kA, vA] of Object.entries(recordA)) {
    const vB = recordB[kA];
    if (vA !== vB) {
      difference[kA] = vA;
    }
  }
  return difference;
}
export const getVisibilityStateParser = (defaults: Record<string, boolean>) => {
  return createParser<z.infer<typeof VisibilityStateSchema>>({
    parse: (value) => {
      try {
        /* eslint-disable @typescript-eslint/no-unsafe-assignment */
        const parsed = JSON.parse(value);
        const result = VisibilityStateSchema.safeParse(parsed);
        return {
          ...defaults,
          ...(result.success ? result.data : {}),
        };
      } catch {
        return null;
      }
    },
    serialize: (value) => {
      return JSON.stringify(findInADiffThanB(value, defaults));
    },
    eq: (a, b) => {
      const diffA = findInADiffThanB(a, defaults);
      const diffB = findInADiffThanB(b, defaults);
      for (const [k, v] of Object.entries(diffA)) {
        if (diffB[k] !== v) {
          return false;
        }
      }
      for (const [k, v] of Object.entries(diffB)) {
        if (diffA[k] !== v) {
          return false;
        }
      }
      return true;
    },
  });
};

Required modifications to use-data-table

...
  const [columnVisibility, setColumnVisibility] = useQueryState(
    "view",
    getVisibilityStateParser(initialState?.columnVisibility ?? {})
      .withOptions(queryStateOptions)
      .withDefault(initialState?.columnVisibility ?? {}),
  );
...

    onColumnVisibilityChange: (v) => {
      void setColumnVisibility(v);
    },

Providing defaults to the hook

const { table } = useDataTable({
    ...
    initialState: {
      columnVisibility: {
        col_a: true,
        col_b: false,
        // ...
      },
    },
    ...
  });

@dcyoung dcyoung closed this as completed Mar 4, 2025
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