Skip to content

Instantly share code, notes, and snippets.

@emkis
Last active December 6, 2023 22:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emkis/4ffbb60fb0d2f499174b5ce3b838f801 to your computer and use it in GitHub Desktop.
Save emkis/4ffbb60fb0d2f499174b5ce3b838f801 to your computer and use it in GitHub Desktop.
Simple abstraction for managing safe query params
export type SerializedString = string;
export type SafeQueryParamOptions<Value> = {
getUnsafeQueryParam(): SerializedString | undefined;
serialize(safeValue: Value): SerializedString;
deserialize(unsafeValue: SerializedString): Value;
parse(unsafeValue: Value): Value;
onParseError(error: unknown): void;
};
export type SafeQueryParam<Value> = {
value?: Value;
serialize(safeValue: Value): SerializedString;
};
export function createSafeQueryParam<Value>(
options: SafeQueryParamOptions<Value>,
): SafeQueryParam<Value> {
const { getUnsafeQueryParam, serialize, deserialize, parse, onParseError } = options;
function computeValue(): Value | undefined {
const unsafeQueryParam = getUnsafeQueryParam();
if (unsafeQueryParam === undefined) return undefined;
try {
const deserializedValue = deserialize(unsafeQueryParam);
return parse(deserializedValue);
} catch (error: unknown) {
onParseError(error);
}
}
return {
serialize,
get value() {
return computeValue();
},
};
}
import moment from 'moment';
import { useQueryParams } from 'hooks/useQueryParams';
import type { QueryParams } from './query-params';
import { identity } from 'lodash';
import { createSafeQueryParam } from './create-safe-query-param';
import { z } from 'zod';
const periodSchema = z.enum(['today', 'yesterday', 'this_week', 'this_month', 'last_3_months']);
const dateSchema = z.date();
export function Period() {
const queryParams = useQueryParams<QueryParams>();
const periodQueryParam = createSafeQueryParam<PeriodPreset>({
serialize: identity,
deserialize: identity,
getUnsafeQueryParam: () => queryParams.value.period,
parse: (unsafeValue) => periodSchema.parse(unsafeValue),
onParseError: onPeriodReset,
});
const fromQueryParam = createSafeQueryParam<Date>({
serialize: serializeDate,
deserialize: deserializeDate,
getUnsafeQueryParam: () => queryParams.value.from,
parse: (unsafeValue) => dateSchema.parse(unsafeValue),
onParseError: resetRange,
});
const toQueryParam = createSafeQueryParam<Date>({
serialize: serializeDate,
deserialize: deserializeDate,
getUnsafeQueryParam: () => queryParams.value.to,
parse: (unsafeValue) => dateSchema.parse(unsafeValue),
onParseError: resetRange,
});
function resetRange() {
queryParams.set({ from: undefined, to: undefined });
}
function onPeriodReset() {
queryParams.set({ from: undefined, to: undefined, period: undefined });
}
return 'JSX';
}
const dateFormat = 'YYYY-MM-DD';
function serializeDate(payload: Date) {
return moment(payload).format(dateFormat);
}
function deserializeDate(payload: string) {
return moment(payload, dateFormat).toDate();
}

Updates

06/12/2023

This API works, but is not opmitised for React, on every render it will read query query param, deserialise and parse the values. What would be great is to only recompute the value when the query params change.

I believe having a hook we can control when the re-render happen more easily, and we can also manage the references internally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment