useQuerystringState.ts 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import {useCallback, useEffect, useState} from 'react';
  2. import {browserHistory} from 'react-router';
  3. import {useLocation} from 'sentry/utils/useLocation';
  4. interface UseQuerystringStateOptions {
  5. key: string;
  6. initialState?: string | string[];
  7. }
  8. export function useQuerystringState<T extends string | string[] = string>({
  9. key,
  10. initialState,
  11. }: UseQuerystringStateOptions) {
  12. const location = useLocation();
  13. const [state, setState] = useState<T | undefined>(
  14. (location.query[key] as T) ?? (initialState as T)
  15. );
  16. const createLocationDescriptor = useCallback(
  17. (nextState: T | undefined) => {
  18. // we can't use the result of `useLocation` here
  19. // if there are multiple instances of `useQuerystringState` firing at once
  20. // the value of location will be stale in the callback
  21. const currentLocation = browserHistory.getCurrentLocation();
  22. return {
  23. ...currentLocation,
  24. query: {
  25. ...currentLocation.query,
  26. [key]: nextState,
  27. },
  28. };
  29. },
  30. [key]
  31. );
  32. const setQueryStringState = useCallback(
  33. (nextState: T | undefined) => {
  34. browserHistory.replace(createLocationDescriptor(nextState));
  35. setState(nextState);
  36. },
  37. [createLocationDescriptor]
  38. );
  39. useEffect(() => {
  40. const removeListener = browserHistory.listenBefore(nextLocation => {
  41. const currentLocation = browserHistory.getCurrentLocation();
  42. // if the next location is a different page altogether
  43. // cleanup the querystring key to ensures querystring's aren't unintentionally passed around pages
  44. if (
  45. currentLocation.pathname !== nextLocation.pathname &&
  46. key in nextLocation.query
  47. ) {
  48. delete nextLocation.query[key];
  49. return;
  50. }
  51. });
  52. return removeListener;
  53. }, [key]);
  54. return [state, setQueryStringState, createLocationDescriptor] as [
  55. T | undefined,
  56. typeof setQueryStringState,
  57. typeof createLocationDescriptor
  58. ];
  59. }