Skip to content

Commit

Permalink
custom useParams and useSearchParams
Browse files Browse the repository at this point in the history
  • Loading branch information
SKairinos committed Jul 5, 2024
1 parent 0e476f0 commit 4a28a9d
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 38 deletions.
81 changes: 77 additions & 4 deletions src/hooks/router.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
import {
useNavigate as _useNavigate,
useSearchParams,
useParams as _useParams,
useSearchParams as _useSearchParams,
type NavigateOptions,
type To as NavigateTo,
type Params,
} from "react-router-dom"
import { object as objectSchema, type ObjectShape } from "yup"

import { type PageState } from "../components/page"
import { type ReadOnly } from "../utils/router"
import {
tryValidateSync,
type ObjectSchemaFromShape,
type TryValidateSyncOnErrorRT,
type TryValidateSyncOptions,
type TryValidateSyncRT,
} from "../utils/schema"

export function useNavigate(): <
State extends Record<string, any> = Record<string, any>,
Expand All @@ -17,7 +28,7 @@ export function useNavigate(): <
},
) => void {
const navigate = _useNavigate()
const searchParams = useSearchParamEntries()
const searchParams = useSearchParams()

return (to, options) => {
const { next = true, ..._options } = options || {}
Expand All @@ -26,6 +37,68 @@ export function useNavigate(): <
}
}

export function useSearchParamEntries() {
return Object.fromEntries(useSearchParams()[0].entries())
// -----------------------------------------------------------------------------
// Use Search Params
// -----------------------------------------------------------------------------

export function useSearchParams(): { [k: string]: string }

export function useSearchParams<
OnErrorRT extends TryValidateSyncOnErrorRT<ObjectSchemaFromShape<Shape>>,
Shape extends ObjectShape = {},
>(
shape: Shape,
validateOptions?: TryValidateSyncOptions<
ObjectSchemaFromShape<Shape>,
OnErrorRT
>,
): TryValidateSyncRT<ObjectSchemaFromShape<Shape>, OnErrorRT>

export function useSearchParams<
OnErrorRT extends TryValidateSyncOnErrorRT<ObjectSchemaFromShape<Shape>>,
Shape extends ObjectShape = {},
>(
shape?: Shape,
validateOptions?: TryValidateSyncOptions<
ObjectSchemaFromShape<Shape>,
OnErrorRT
>,
) {
const searchParams = Object.fromEntries(_useSearchParams()[0].entries())
if (!shape) return searchParams

return tryValidateSync(searchParams, objectSchema(shape), validateOptions)
}

// -----------------------------------------------------------------------------
// Use Params
// -----------------------------------------------------------------------------

export function useParams(): ReadOnly<Params<string>>

export function useParams<
OnErrorRT extends TryValidateSyncOnErrorRT<ObjectSchemaFromShape<Shape>>,
Shape extends ObjectShape = {},
>(
shape: Shape,
validateOptions?: TryValidateSyncOptions<
ObjectSchemaFromShape<Shape>,
OnErrorRT
>,
): TryValidateSyncRT<ObjectSchemaFromShape<Shape>, OnErrorRT>

export function useParams<
OnErrorRT extends TryValidateSyncOnErrorRT<ObjectSchemaFromShape<Shape>>,
Shape extends ObjectShape = {},
>(
shape?: Shape,
validateOptions?: TryValidateSyncOptions<
ObjectSchemaFromShape<Shape>,
OnErrorRT
>,
) {
const params = _useParams()
if (!shape) return params

return tryValidateSync(params, objectSchema(shape), validateOptions)
}
4 changes: 4 additions & 0 deletions src/utils/router.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { generatePath } from "react-router-dom"

export type ReadOnly<T> = {
readonly [P in keyof T]: T[P]
}

export type Parameters = Record<string, string>

export interface Path {
Expand Down
96 changes: 62 additions & 34 deletions src/utils/schema.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,80 @@
import {
type InferType,
type ValidateOptions,
ValidationError,
type AnyObject,
type DefaultFromShape,
type InferType,
type MakePartial,
type ObjectSchema,
type ObjectShape,
type Schema,
type TypeFromShape,
type ValidateOptions,
} from "yup"

export function tryValidateSync<Schema extends ObjectSchema<any>>(
export type _<T> = T extends {}
? {
[k in keyof T]: T[k]
}
: T

export type MakeKeysOptional<T> = T extends AnyObject ? _<MakePartial<T>> : T

export type ObjectFromShape<Shape extends ObjectShape> = MakeKeysOptional<
_<TypeFromShape<Shape, AnyObject>>
>

export type ObjectSchemaFromShape<Shape extends ObjectShape> = ObjectSchema<
_<TypeFromShape<Shape, AnyObject>>,
AnyObject,
_<DefaultFromShape<Shape>>,
""
>

// -----------------------------------------------------------------------------
// Try Validate Sync
// -----------------------------------------------------------------------------

export type TryValidateSyncOnErrorRT<S extends Schema> = InferType<S> | void

export type TryValidateSyncRT<
S extends Schema,
OnErrorRT extends TryValidateSyncOnErrorRT<S>,
> = OnErrorRT extends InferType<S> ? InferType<S> : InferType<S> | undefined

export type TryValidateSyncOptions<
S extends Schema,
OnErrorRT extends TryValidateSyncOnErrorRT<S>,
> = ValidateOptions & {
onError?: (error: ValidationError) => OnErrorRT
}

export function tryValidateSync<S extends Schema>(
value: any,
schema: Schema,
kwArgs?: {
options?: ValidateOptions
},
): InferType<Schema> | undefined
schema: S,
options?: ValidateOptions,
): InferType<S> | undefined

export function tryValidateSync<
Schema extends ObjectSchema<any>,
OnErrorRT extends InferType<Schema> | void,
S extends Schema,
OnErrorRT extends TryValidateSyncOnErrorRT<S>,
>(
value: any,
schema: Schema,
kwArgs?: {
options?: ValidateOptions
schema: S,
options?: ValidateOptions & {
onError: (error: ValidationError) => OnErrorRT
},
): OnErrorRT extends InferType<Schema>
? InferType<Schema>
: InferType<Schema> | undefined
): TryValidateSyncRT<S, OnErrorRT>

export function tryValidateSync<
Schema extends ObjectSchema<any>,
OnErrorRT extends InferType<Schema> | void,
>(
value: any,
schema: Schema,
kwArgs?: {
options?: ValidateOptions
onError?: (error: ValidationError) => OnErrorRT
},
): InferType<Schema> | undefined {
S extends Schema,
OnErrorRT extends TryValidateSyncOnErrorRT<S>,
>(value: any, schema: S, options?: TryValidateSyncOptions<S, OnErrorRT>) {
const { onError, ...validateOptions } = options || {}

try {
return schema.validateSync(value, kwArgs?.options)
return schema.validateSync(value, validateOptions)
} catch (error) {
if (!(error instanceof ValidationError)) {
throw error
} else if (kwArgs?.onError !== undefined) {
return kwArgs.onError(error) as InferType<Schema> | undefined
}
if (!(error instanceof ValidationError)) throw error
else if (onError) return onError(error)
}

return undefined
}

0 comments on commit 4a28a9d

Please sign in to comment.