nuqs icon indicating copy to clipboard operation
nuqs copied to clipboard

`parser.defaultValue` is `undefined` instead of `null`

Open IgnisDa opened this issue 1 month ago • 2 comments

Context

What's your version of nuqs?

"nuqs": "2.8.3"

What framework are you using?

  • ❌ Next.js (app router)
  • ❌ Next.js (pages router)
  • ❌ React SPA (no router)
  • ❌ Remix
  • ✅ React Router
  • ❌ Other (please specify)

Which version of your framework are you using?

"react-router": "7.10.1",
"react": "19.2.1",

Description

I am trying to implement the function mentioned in https://github.com/47ng/nuqs/discussions/860 which mostly works for detecting when the default value of URL keys have changed. But I am hitting an edge case where the keys without defaults are set as undefined so as a result the function always returns false.

Reproduction

Here is a minimal reproduction:

import {
	type inferParserType,
	parseAsString,
	parseAsStringEnum,
	type ParserMap,
	useQueryStates,
} from "nuqs";

enum Animal {
	Dog = "Dog",
	Cat = "Cat",
	Bird = "Bird",
}

enum Color {
	Red = "Red",
	Green = "Green",
	Blue = "Blue",
}

enum Size {
	Small = "Small",
	Medium = "Medium",
	Large = "Large",
}

const defaultQueryState = {
	query: parseAsString.withDefault(""),
	color: parseAsStringEnum(Object.values(Color)),
	animal: parseAsStringEnum(Object.values(Animal)),
	size: parseAsStringEnum(Object.values(Size)).withDefault(Size.Medium),
};

function isDefaultState<Parsers extends ParserMap>(
	parsers: Parsers,
	values: inferParserType<Parsers>,
): boolean {
	for (const [key, parser] of Object.entries(parsers)) {
		console.log({
			key,
			value: values[key],
			defaultValue: parsers[key].defaultValue,
		});
		if (!parser.eq(values[key], parsers[key].defaultValue)) {
			return false;
		}
	}
	return true;
}

export default function Page() {
	const [filters] = useQueryStates(defaultQueryState);
	const isDefault = isDefaultState(defaultQueryState, filters);

	return (
		<div>
			<p>Testing Page</p>
			<p>Current Filters: {JSON.stringify(filters)}</p>
			<p>Is Default State: {JSON.stringify({ isDefault })}</p>
		</div>
	);
}

Browser view:

Image

Console view:

Image

(the function is returning early since the check fails in the second loop, thus the other keys are not logged)

Expected behavior

I expect the function to return true.

IgnisDa avatar Dec 10 '25 02:12 IgnisDa

That's not a bug, null is the value that's returned when there is no default value specified (ie: when it's undefined), so you'd have to take it into account in your function:

function isDefaultState<Parsers extends ParserMap>(
	parsers: Parsers,
	values: inferParserType<Parsers>,
): boolean {
	for (const [key, parser] of Object.entries(parsers)) {
		console.log({
			key,
			value: values[key],
			defaultValue: parsers[key].defaultValue,
		});
+		if (parsers[key].defaultValue === undefined && values[key] !== null) {
+			return false
+		}
		if (!parser.eq(values[key], parsers[key].defaultValue)) {
			return false;
		}
	}
	return true;
}

franky47 avatar Dec 10 '25 15:12 franky47

Is null is the default value then shouldn’t parser.defaultValue also return null?

IgnisDa avatar Dec 10 '25 16:12 IgnisDa