@datalayer/core
Version:
[](https://datalayer.io)
113 lines (112 loc) • 4.35 kB
JavaScript
/*
* 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;
};