UNPKG

@datalayer/core

Version:

[![Datalayer](https://assets.datalayer.tech/datalayer-25.svg)](https://datalayer.io)

113 lines (112 loc) 4.35 kB
/* * Copyright (c) 2023-2025 Datalayer, Inc. * Distributed under the terms of the Modified BSD License. */ import { useEffect, useState, useMemo } from 'react'; import { useParamsRR } from '../navigation/adapters/react-router'; import { useParamsNext, useSearchParamsNext, } from '../navigation/adapters/nextjs'; /** * Hook to get URL parameters * Works with Next.js App Router, React Router, or falls back to URL parsing */ export const useParams = () => { // State for fallback URL search params const [searchParams, setSearchParams] = useState({}); // Detect environment const isNextJs = typeof window !== 'undefined' && !!window.__NEXT_DATA__; const isClient = typeof window !== 'undefined'; // Try to get params from different sources let routeParams = {}; const queryParams = {}; let paramsSource = 'fallback'; // 1. Try Next.js first (if detected) if (isNextJs && useParamsNext) { try { // eslint-disable-next-line react-hooks/rules-of-hooks routeParams = useParamsNext() || {}; paramsSource = 'nextjs'; // Also get search params in Next.js if (useSearchParamsNext) { try { // eslint-disable-next-line react-hooks/rules-of-hooks const searchParamsObj = useSearchParamsNext(); if (searchParamsObj) { searchParamsObj.forEach((value, key) => { queryParams[key] = value; }); } } catch { // Not in Next.js context or search params not available } } } catch { // Not in Next.js router context } } // 2. Try React Router (if not Next.js) if (paramsSource === 'fallback' && !isNextJs && useParamsRR) { try { // eslint-disable-next-line react-hooks/rules-of-hooks routeParams = useParamsRR() || {}; paramsSource = 'react-router'; } catch { // Not in React Router context } } // 3. Get URL search params (for React Router and fallback) useEffect(() => { if (!isClient || paramsSource === 'nextjs') return; const updateSearchParams = () => { const params = {}; const urlParams = new URLSearchParams(window.location.search); urlParams.forEach((value, key) => { params[key] = value; }); setSearchParams(params); }; updateSearchParams(); // Listen for URL changes window.addEventListener('popstate', updateSearchParams); window.addEventListener('pushstate', updateSearchParams); window.addEventListener('replacestate', updateSearchParams); return () => { window.removeEventListener('popstate', updateSearchParams); window.removeEventListener('pushstate', updateSearchParams); window.removeEventListener('replacestate', updateSearchParams); }; }, [isClient, paramsSource]); // Combine all params with proper memoization to prevent re-renders const combinedParams = useMemo(() => { const result = {}; // Add route params for (const [key, value] of Object.entries(routeParams)) { if (value !== undefined) { // Handle array values (Next.js catch-all routes) result[key] = Array.isArray(value) ? value.join('/') : String(value); } } // Add query params (Next.js) for (const [key, value] of Object.entries(queryParams)) { result[key] = value; } // Add search params (React Router and fallback) if (paramsSource !== 'nextjs') { for (const [key, value] of Object.entries(searchParams)) { result[key] = value; } } // Fallback route params are not supported without router configuration return result; }, [ // Use JSON.stringify for deep comparison JSON.stringify(routeParams), JSON.stringify(queryParams), JSON.stringify(searchParams), paramsSource, ]); return combinedParams; };