1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-06 00:07:44 +01:00
unleash.unleash/frontend/src/hooks/usePersistentTableState.ts
Thomas Heartman 442327eb07
fix: don't add project flag table state config to browser history (#6824)
This change specifies the update type as `replace` for the
`useQueryParams` hook used to set table state.

Primarily, this prevents the column selection from being added to the
browser
history and more importantly prevents you from changing your config by
navigating through browser history.

However, this also affects other table state, such as changing sorting
order etc. These will also no longer be added to the browser history.

---

Bug description:

In the project flag table, you can select which env columns to show.
However, adding and removing these envs get added as steps in your
browser history. This means that if you add 3 envs, you:

1. have to go back three times to get back to the previous page
2. In doing so, you also inadvertently revert the choices you mean,
which can be confusing.

Steps to reproduce:

1. Navigate to the project screen
2. Use the column selector to add/remove projects. Notice that the URL
changes for each selection you make.
3. After making one or more changes, use the browser's
back-functionality. Notice that you stay on the same page but that the
selected envs (and the URL) change.
2024-04-12 10:58:17 +02:00

92 lines
3.1 KiB
TypeScript

import { useEffect, useCallback, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import { createLocalStorage } from 'utils/createLocalStorage';
import { encodeQueryParams, useQueryParams } from 'use-query-params';
import type { QueryParamConfigMap } from 'serialize-query-params/src/types';
import { reorderObject } from '../utils/reorderObject';
const usePersistentSearchParams = <T extends QueryParamConfigMap>(
key: string,
queryParamsDefinition: T,
) => {
const [searchParams, setSearchParams] = useSearchParams();
const { value, setValue } = createLocalStorage(key, {});
useEffect(() => {
const params = Object.fromEntries(searchParams.entries());
if (Object.keys(params).length > 0) {
return;
}
if (Object.keys(value).length === 0) {
return;
}
setSearchParams(
encodeQueryParams(queryParamsDefinition, value) as Record<
string,
string
>,
{ replace: true },
);
}, []);
return setValue;
};
export const usePersistentTableState = <T extends QueryParamConfigMap>(
key: string,
queryParamsDefinition: T,
) => {
const updateStoredParams = usePersistentSearchParams(
key,
queryParamsDefinition,
);
const [tableState, setTableStateInternal] = useQueryParams(
queryParamsDefinition,
{ updateType: 'replaceIn' },
);
const [searchParams] = useSearchParams();
const orderedTableState = useMemo(() => {
return reorderObject(tableState, [...searchParams.keys()]);
}, [searchParams, tableState, reorderObject]);
type SetTableStateInternalParam = Parameters<
typeof setTableStateInternal
>[0];
const setTableState = useCallback(
(newState: SetTableStateInternalParam) => {
if (!queryParamsDefinition.offset) {
return setTableStateInternal(newState);
}
if (typeof newState === 'function') {
setTableStateInternal((prevState) => {
const updatedState = (newState as Function)(prevState);
return queryParamsDefinition.offset
? {
offset: queryParamsDefinition.offset.decode('0'),
...updatedState,
}
: updatedState;
});
} else {
const updatedState = queryParamsDefinition.offset
? {
offset: queryParamsDefinition.offset.decode('0'),
...newState,
}
: newState;
setTableStateInternal(updatedState);
}
},
[setTableStateInternal, queryParamsDefinition.offset],
);
useEffect(() => {
const { offset, ...rest } = orderedTableState;
updateStoredParams(rest);
}, [JSON.stringify(orderedTableState)]);
return [orderedTableState, setTableState] as const;
};