useNavigate.tsx 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. import {useCallback, useEffect, useRef} from 'react';
  2. import type {LocationDescriptor} from 'history';
  3. import normalizeUrl from 'sentry/utils/url/normalizeUrl';
  4. import useRouter from './useRouter';
  5. type NavigateOptions = {
  6. replace?: boolean;
  7. state?: any;
  8. };
  9. interface ReactRouter3Navigate {
  10. (to: LocationDescriptor, options?: NavigateOptions): void;
  11. (delta: number): void;
  12. }
  13. /**
  14. * Returns an imperative method for changing the location. Used by `<Link>`s, but
  15. * may also be used by other elements to change the location.
  16. *
  17. * @see https://reactrouter.com/hooks/use-navigate
  18. */
  19. export function useNavigate() {
  20. const router = useRouter();
  21. const hasMountedRef = useRef(false);
  22. useEffect(() => {
  23. hasMountedRef.current = true;
  24. });
  25. const navigate = useCallback<ReactRouter3Navigate>(
  26. (to: LocationDescriptor | number, options: NavigateOptions = {}) => {
  27. if (!hasMountedRef.current) {
  28. throw new Error(
  29. `You should call navigate() in a React.useEffect(), not when your component is first rendered.`
  30. );
  31. }
  32. if (typeof to === 'number') {
  33. return router.go(to);
  34. }
  35. const normalizedTo = normalizeUrl(to);
  36. const nextState: LocationDescriptor =
  37. typeof normalizedTo === 'string'
  38. ? {
  39. pathname: normalizedTo,
  40. state: options.state,
  41. }
  42. : {
  43. ...normalizedTo,
  44. state: options.state,
  45. };
  46. if (options.replace) {
  47. return router.replace(nextState);
  48. }
  49. return router.push(nextState);
  50. },
  51. [router]
  52. );
  53. return navigate;
  54. }