@granite-js/react-native 1.0.27 → 1.0.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # @granite-js/react-native
2
2
 
3
+ ## 1.0.29
4
+
5
+ ### Patch Changes
6
+
7
+ - 1b504a1: feat(router): Route.useSetParams
8
+ - @granite-js/blur-view@1.0.29
9
+ - @granite-js/brownfield-module@1.0.29
10
+ - @granite-js/cli@1.0.29
11
+ - @granite-js/jest@1.0.29
12
+ - @granite-js/mpack@1.0.29
13
+ - @granite-js/native@1.0.29
14
+ - @granite-js/plugin-core@1.0.29
15
+ - @granite-js/style-utils@1.0.29
16
+ - @granite-js/video@1.0.29
17
+
18
+ ## 1.0.28
19
+
20
+ ### Patch Changes
21
+
22
+ - 494c13e: feat: schemeUri to `getSchemeUri`
23
+ - Updated dependencies [494c13e]
24
+ - @granite-js/brownfield-module@1.0.28
25
+ - @granite-js/native@1.0.28
26
+ - @granite-js/blur-view@1.0.28
27
+ - @granite-js/jest@1.0.28
28
+ - @granite-js/style-utils@1.0.28
29
+ - @granite-js/cli@1.0.28
30
+ - @granite-js/mpack@1.0.28
31
+ - @granite-js/plugin-core@1.0.28
32
+ - @granite-js/video@1.0.28
33
+
3
34
  ## 1.0.27
4
35
 
5
36
  ### Patch Changes
@@ -23,7 +23,7 @@ export interface GraniteProps {
23
23
  * @description
24
24
  * The initial scheme of the app.
25
25
  */
26
- initialScheme?: string;
26
+ initialScheme?: string | (() => string);
27
27
  /**
28
28
  * @description
29
29
  * The function to set the iOS swipe gesture enabled.
@@ -1,25 +1,3 @@
1
- /**
2
- * @public
3
- * @name getSchemeUri
4
- * @category Environment Check
5
- * @kind function
6
- * @description Returns the scheme value when first entering the screen. URI changes due to page navigation are not reflected.
7
- * @returns {string} Returns the scheme value when first entering the screen.
8
- *
9
- * @example
10
- * ### Get initial scheme value
11
- *
12
- * ```tsx
13
- * import { getSchemeUri } from '@granite-js/react-native';
14
- * import { Text } from 'react-native';
15
- *
16
- * function MyPage() {
17
- * const schemeUri = getSchemeUri();
18
- *
19
- * return <Text>Initial scheme value: {schemeUri}</Text>
20
- * }
21
- * ```
22
- */
23
1
  declare function getSchemeUri(): string;
24
2
 
25
3
  export { getSchemeUri };
@@ -27,7 +27,11 @@ module.exports = __toCommonJS(constant_bridges_exports);
27
27
  // src/native-modules/natives/getSchemeUri.ts
28
28
  var import_brownfield_module = require("@granite-js/brownfield-module");
29
29
  function getSchemeUri() {
30
- return import_brownfield_module.GraniteBrownfieldModule.getConstants().schemeUri;
30
+ try {
31
+ return import_brownfield_module.GraniteBrownfieldModule.getSchemeUri();
32
+ } catch {
33
+ return import_brownfield_module.GraniteBrownfieldModule.getConstants().schemeUri;
34
+ }
31
35
  }
32
36
  // Annotate the CommonJS export names for ESM import in node:
33
37
  0 && (module.exports = {
@@ -1,7 +1,11 @@
1
1
  // src/native-modules/natives/getSchemeUri.ts
2
2
  import { GraniteBrownfieldModule } from "@granite-js/brownfield-module";
3
3
  function getSchemeUri() {
4
- return GraniteBrownfieldModule.getConstants().schemeUri;
4
+ try {
5
+ return GraniteBrownfieldModule.getSchemeUri();
6
+ } catch {
7
+ return GraniteBrownfieldModule.getConstants().schemeUri;
8
+ }
5
9
  }
6
10
  export {
7
11
  getSchemeUri
@@ -1,23 +1 @@
1
- /**
2
- * @public
3
- * @name getSchemeUri
4
- * @category Environment Check
5
- * @kind function
6
- * @description Returns the scheme value when first entering the screen. URI changes due to page navigation are not reflected.
7
- * @returns {string} Returns the scheme value when first entering the screen.
8
- *
9
- * @example
10
- * ### Get initial scheme value
11
- *
12
- * ```tsx
13
- * import { getSchemeUri } from '@granite-js/react-native';
14
- * import { Text } from 'react-native';
15
- *
16
- * function MyPage() {
17
- * const schemeUri = getSchemeUri();
18
- *
19
- * return <Text>Initial scheme value: {schemeUri}</Text>
20
- * }
21
- * ```
22
- */
23
1
  export declare function getSchemeUri(): string;
@@ -17,9 +17,11 @@ export interface RegisterScreenInput {
17
17
  }
18
18
  export interface RegisterScreen {
19
19
  }
20
- export type NavigationProps = NativeStackNavigationProp<keyof RegisterScreenInput extends never ? ParamListBase : RegisterScreenInput>;
20
+ type RegisteredParamList = keyof RegisterScreenInput extends never ? ParamListBase : RegisterScreenInput;
21
+ export type NavigationProps = NativeStackNavigationProp<RegisteredParamList>;
21
22
  export declare function useNavigation(): NavigationProps;
22
- export type RouteHooksOptions<TScreen extends keyof RegisterScreen> = {
23
+ type RouteHookScreen = keyof RegisterScreenInput | keyof RegisterScreen;
24
+ export type RouteHooksOptions<TScreen extends RouteHookScreen> = {
23
25
  from: TScreen;
24
26
  strict?: true;
25
27
  } | {
@@ -34,7 +36,7 @@ export declare const routeMap: Map<keyof RegisterScreenInput, {
34
36
  params: any;
35
37
  }) => NativeStackNavigationOptions);
36
38
  }>;
37
- export declare function useMatchOptions<TScreen extends keyof RegisterScreen>(options: RouteHooksOptions<TScreen>): {
39
+ export declare function useMatchOptions<TScreen extends RouteHookScreen>(options: RouteHooksOptions<TScreen>): {
38
40
  parserParams: ((params: Record<string, unknown>) => Record<string, unknown>) | typeof defaultParserParams;
39
41
  validateParams?: StandardSchemaV1<any, any> | ((params: Readonly<object | undefined>) => any) | undefined;
40
42
  } | null;
@@ -45,19 +47,33 @@ export declare function useParams<TScreen extends keyof RegisterScreen>(options:
45
47
  export declare function useParams(options: {
46
48
  strict: false;
47
49
  }): Readonly<object | undefined>;
48
- export declare function createRoute<TSchema extends StandardSchemaV1<any, any>>(path: keyof RegisterScreenInput, options: Omit<RouteOptions<any>, 'validateParams'> & {
49
- validateParams: TSchema;
50
- }): {
50
+ type SetParamsFunction<TParams> = (params: TParams extends undefined ? undefined : Partial<TParams>) => void;
51
+ type ReplaceParamsFunction<TParams> = (params: TParams extends undefined ? undefined : TParams) => void;
52
+ type CreatedRoute<TInput, TOutput> = {
51
53
  _path: keyof RegisterScreenInput;
52
54
  useNavigation: typeof useNavigation;
53
- useParams: () => InferOutput<TSchema>;
54
- _inputType: InferInput<TSchema>;
55
- _outputType: InferOutput<TSchema>;
56
- };
57
- export declare function createRoute<T extends Readonly<object | undefined>>(path: keyof RegisterScreenInput, options: RouteOptions<T>): {
58
- _path: keyof RegisterScreenInput;
59
- useNavigation: typeof useNavigation;
60
- useParams: () => T;
61
- _inputType: T;
62
- _outputType: T;
55
+ useParams: () => TOutput;
56
+ useSetParams: () => SetParamsFunction<TInput>;
57
+ useReplaceParams: () => ReplaceParamsFunction<TInput>;
58
+ _inputType: TInput;
59
+ _outputType: TOutput;
63
60
  };
61
+ export declare function useSetParams<TScreen extends keyof RegisterScreenInput>(options: {
62
+ from: TScreen;
63
+ strict?: true;
64
+ }): SetParamsFunction<RegisterScreenInput[TScreen]>;
65
+ export declare function useSetParams(options: {
66
+ strict: false;
67
+ }): SetParamsFunction<Readonly<object | undefined>>;
68
+ export declare function useReplaceParams<TScreen extends keyof RegisterScreenInput>(options: {
69
+ from: TScreen;
70
+ strict?: true;
71
+ }): ReplaceParamsFunction<RegisterScreenInput[TScreen]>;
72
+ export declare function useReplaceParams(options: {
73
+ strict: false;
74
+ }): ReplaceParamsFunction<Readonly<object | undefined>>;
75
+ export declare function createRoute<TSchema extends StandardSchemaV1<any, any>>(path: keyof RegisterScreenInput, options: Omit<RouteOptions<any>, 'validateParams'> & {
76
+ validateParams: TSchema;
77
+ }): CreatedRoute<InferInput<TSchema>, InferOutput<TSchema>>;
78
+ export declare function createRoute<T extends Readonly<object | undefined>>(path: keyof RegisterScreenInput, options: RouteOptions<T>): CreatedRoute<T, T>;
79
+ export {};
@@ -14,6 +14,9 @@ declare module './createRoute' {
14
14
  '/test-with-defaults': {
15
15
  animation?: boolean;
16
16
  };
17
+ '/test-optional': {
18
+ uri?: string;
19
+ };
17
20
  }
18
21
  interface RegisterScreen {
19
22
  '/test': {
@@ -30,6 +33,9 @@ declare module './createRoute' {
30
33
  '/test-with-defaults': {
31
34
  animation: boolean;
32
35
  };
36
+ '/test-optional': {
37
+ uri?: string;
38
+ };
33
39
  }
34
40
  }
35
41
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@granite-js/react-native",
3
- "version": "1.0.27",
3
+ "version": "1.0.29",
4
4
  "description": "The Granite Framework",
5
5
  "bin": {
6
6
  "granite": "./bin/cli.js"
@@ -91,7 +91,7 @@
91
91
  "@babel/core": "7.28.5",
92
92
  "@babel/preset-env": "7.28.5",
93
93
  "@babel/preset-typescript": "7.28.5",
94
- "@granite-js/native": "1.0.27",
94
+ "@granite-js/native": "1.0.29",
95
95
  "@testing-library/dom": "^10.4.0",
96
96
  "@testing-library/react": "^16.1.0",
97
97
  "@types/babel__core": "^7",
@@ -113,21 +113,21 @@
113
113
  "zod": "^4.1.12"
114
114
  },
115
115
  "peerDependencies": {
116
- "@granite-js/native": "1.0.27",
116
+ "@granite-js/native": "1.0.29",
117
117
  "@types/react": "*",
118
118
  "brick-module": "*",
119
119
  "react": "*",
120
120
  "react-native": "*"
121
121
  },
122
122
  "dependencies": {
123
- "@granite-js/blur-view": "1.0.27",
124
- "@granite-js/brownfield-module": "1.0.27",
125
- "@granite-js/cli": "1.0.27",
126
- "@granite-js/jest": "1.0.27",
127
- "@granite-js/mpack": "1.0.27",
128
- "@granite-js/plugin-core": "1.0.27",
129
- "@granite-js/style-utils": "1.0.27",
130
- "@granite-js/video": "1.0.27",
123
+ "@granite-js/blur-view": "1.0.29",
124
+ "@granite-js/brownfield-module": "1.0.29",
125
+ "@granite-js/cli": "1.0.29",
126
+ "@granite-js/jest": "1.0.29",
127
+ "@granite-js/mpack": "1.0.29",
128
+ "@granite-js/plugin-core": "1.0.29",
129
+ "@granite-js/style-utils": "1.0.29",
130
+ "@granite-js/video": "1.0.29",
131
131
  "@standard-schema/spec": "^1.0.0",
132
132
  "es-toolkit": "^1.39.8",
133
133
  "react-native-url-polyfill": "3.0.0"
@@ -31,7 +31,7 @@ export interface GraniteProps {
31
31
  * @description
32
32
  * The initial scheme of the app.
33
33
  */
34
- initialScheme?: string;
34
+ initialScheme?: string | (() => string);
35
35
 
36
36
  /**
37
37
  * @description
@@ -63,7 +63,7 @@ const createApp = () => {
63
63
  }
64
64
 
65
65
  return {
66
- registerApp(
66
+ registerApp(
67
67
  AppContainer: ComponentType<PropsWithChildren<InitialProps>>,
68
68
  { appName, context, router, initialScheme, setIosSwipeGestureEnabled, getInitialUrl }: GraniteProps
69
69
  ): (initialProps: InitialProps) => JSX.Element {
@@ -72,11 +72,13 @@ const createApp = () => {
72
72
  }
73
73
 
74
74
  function Root(initialProps: InitialProps) {
75
+ const initialSchemeValue = (typeof initialScheme === 'function' ? initialScheme() : initialScheme) ?? getSchemeUri();
76
+
75
77
  return (
76
78
  <AppRoot
77
79
  container={AppContainer}
78
80
  initialProps={initialProps}
79
- initialScheme={initialScheme ?? getSchemeUri()}
81
+ initialScheme={initialSchemeValue}
80
82
  setIosSwipeGestureEnabled={setIosSwipeGestureEnabled}
81
83
  getInitialUrl={getInitialUrl}
82
84
  appName={appName}
@@ -1,27 +1,10 @@
1
- import { GraniteBrownfieldModule } from '@granite-js/brownfield-module';
1
+ import { GraniteBrownfieldModule } from "@granite-js/brownfield-module";
2
2
 
3
- /**
4
- * @public
5
- * @name getSchemeUri
6
- * @category Environment Check
7
- * @kind function
8
- * @description Returns the scheme value when first entering the screen. URI changes due to page navigation are not reflected.
9
- * @returns {string} Returns the scheme value when first entering the screen.
10
- *
11
- * @example
12
- * ### Get initial scheme value
13
- *
14
- * ```tsx
15
- * import { getSchemeUri } from '@granite-js/react-native';
16
- * import { Text } from 'react-native';
17
- *
18
- * function MyPage() {
19
- * const schemeUri = getSchemeUri();
20
- *
21
- * return <Text>Initial scheme value: {schemeUri}</Text>
22
- * }
23
- * ```
24
- */
25
3
  export function getSchemeUri() {
26
- return GraniteBrownfieldModule.getConstants().schemeUri;
27
- }
4
+ try {
5
+ return GraniteBrownfieldModule.getSchemeUri();
6
+ } catch {
7
+ // Fallback to the deprecated `schemeUri` constant for older versions of the native module
8
+ return GraniteBrownfieldModule.getConstants().schemeUri;
9
+ }
10
+ }
@@ -1,6 +1,12 @@
1
1
  import { assertType, describe, it } from 'vitest';
2
2
  import { z } from 'zod';
3
- import { createRoute, useNavigation, useParams } from './createRoute';
3
+ import {
4
+ createRoute,
5
+ useNavigation,
6
+ useParams,
7
+ useReplaceParams,
8
+ useSetParams,
9
+ } from './createRoute';
4
10
 
5
11
  declare module './createRoute' {
6
12
  interface RegisterScreenInput {
@@ -18,6 +24,9 @@ declare module './createRoute' {
18
24
  '/test-with-defaults': {
19
25
  animation?: boolean;
20
26
  };
27
+ '/test-optional': {
28
+ uri?: string;
29
+ };
21
30
  }
22
31
 
23
32
  interface RegisterScreen {
@@ -35,6 +44,9 @@ declare module './createRoute' {
35
44
  '/test-with-defaults': {
36
45
  animation: boolean;
37
46
  };
47
+ '/test-optional': {
48
+ uri?: string;
49
+ };
38
50
  }
39
51
  }
40
52
 
@@ -78,6 +90,52 @@ describe('createRoute', () => {
78
90
  assertType(useParams({ from: '/test', strict: false }));
79
91
  });
80
92
 
93
+ it('useSetParams and useReplaceParams', () => {
94
+ const setRouteParams = Route.useSetParams();
95
+
96
+ assertType<(params: Partial<{ id: string; name: string }>) => void>(setRouteParams);
97
+ setRouteParams({ id: 'test-id' });
98
+ setRouteParams({ name: 'test-name' });
99
+
100
+ // @ts-expect-error Type error should occur when param type is invalid
101
+ setRouteParams({ id: 123 });
102
+
103
+ // @ts-expect-error Type error should occur when param key is invalid
104
+ setRouteParams({ count: 123 });
105
+
106
+ const setParams = useSetParams({ from: '/test' });
107
+ setParams({ id: 'test-id' });
108
+
109
+ // @ts-expect-error Type error should occur when path is not registered
110
+ assertType(useSetParams({ from: '/abcdefg' }));
111
+
112
+ // @ts-expect-error Type error should occur since 'from' and 'strict: false' are conflicting options
113
+ assertType(useSetParams({ from: '/test', strict: false }));
114
+
115
+ const replaceRouteParams = Route.useReplaceParams();
116
+
117
+ assertType<(params: { id: string; name: string }) => void>(replaceRouteParams);
118
+ replaceRouteParams({ id: 'test-id', name: 'test-name' });
119
+
120
+ // @ts-expect-error Type error should occur when required params are missing
121
+ replaceRouteParams({ id: 'test-id' });
122
+
123
+ // @ts-expect-error Type error should occur when param type is invalid
124
+ replaceRouteParams({ id: 123, name: 'test-name' });
125
+
126
+ // @ts-expect-error Type error should occur when param key is invalid
127
+ replaceRouteParams({ id: 'test-id', name: 'test-name', count: 123 });
128
+
129
+ const replaceParams = useReplaceParams({ from: '/test' });
130
+ replaceParams({ id: 'test-id', name: 'test-name' });
131
+
132
+ // @ts-expect-error Type error should occur when path is not registered
133
+ assertType(useReplaceParams({ from: '/abcdefg' }));
134
+
135
+ // @ts-expect-error Type error should occur since 'from' and 'strict: false' are conflicting options
136
+ assertType(useReplaceParams({ from: '/test', strict: false }));
137
+ });
138
+
81
139
  it('should infer _inputType and _outputType without undefined for function pattern', () => {
82
140
  type InputType = (typeof Route)['_inputType'];
83
141
  type OutputType = (typeof Route)['_outputType'];
@@ -165,10 +223,48 @@ describe('createRoute with StandardSchema', () => {
165
223
 
166
224
  // Should accept exact input type
167
225
  navigation.navigate('/test-schema', { id: 'test', count: 123 });
226
+
227
+ // @ts-expect-error Type error should occur when a required params object is missing
228
+ navigation.navigate('/test-schema');
229
+
230
+ // @ts-expect-error Type error should occur when navigate param type is invalid
231
+ navigation.navigate('/test-schema', { id: 'test', count: 'invalid' });
232
+
233
+ // @ts-expect-error Type error should occur when navigating to an unregistered path
234
+ navigation.navigate('/abcdefg');
235
+
236
+ navigation.navigate('/test-optional', {});
237
+ navigation.navigate('/test-optional', { uri: 'test-uri' });
238
+
239
+ // @ts-expect-error Type error should occur when navigate params include an unknown key
240
+ navigation.navigate('/test-optional', { name: '123' });
241
+
242
+ navigation.popTo('/test-optional', { uri: 'test-uri' });
243
+
244
+ // @ts-expect-error popTo should already reject unknown param keys
245
+ navigation.popTo('/test-optional', { name: '123' });
246
+
247
+ navigation.push('/test-optional', { uri: 'test-uri' });
248
+
249
+ // @ts-expect-error push should reject unknown param keys
250
+ navigation.push('/test-optional', { name: '123' });
251
+
252
+ navigation.replace('/test-optional', { uri: 'test-uri' });
253
+
254
+ // @ts-expect-error replace should reject unknown param keys
255
+ navigation.replace('/test-optional', { name: '123' });
256
+
257
+ // @ts-expect-error push should require params for required route params
258
+ navigation.push('/test-schema');
259
+
260
+ // @ts-expect-error replace should require params for required route params
261
+ navigation.replace('/test-schema');
262
+
263
+ // @ts-expect-error popTo should require params for required route params
264
+ navigation.popTo('/test-schema');
168
265
  });
169
266
 
170
267
  it('should infer output type from transformation', () => {
171
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
172
268
  const RouteWithTransform = createRoute('/test-transform', {
173
269
  component: () => null,
174
270
  validateParams: z.object({
@@ -199,6 +295,24 @@ describe('createRoute with StandardSchema', () => {
199
295
  const navigation = useNavigation();
200
296
  navigation.navigate('/test-transform', { id: 'test-id' });
201
297
 
298
+ const setParams = RouteWithTransform.useSetParams();
299
+ setParams({ id: 'test-id' });
300
+
301
+ // @ts-expect-error setParams should accept input type, not output type
302
+ setParams({ id: 123 });
303
+
304
+ const setParamsFromHook = useSetParams({ from: '/test-transform' });
305
+ setParamsFromHook({ id: 'test-id' });
306
+
307
+ // @ts-expect-error useSetParams should accept input type, not output type
308
+ setParamsFromHook({ id: 123 });
309
+
310
+ const replaceParams = RouteWithTransform.useReplaceParams();
311
+ replaceParams({ id: 'test-id' });
312
+
313
+ // @ts-expect-error useReplaceParams should accept input type, not output type
314
+ replaceParams({ id: 123 });
315
+
202
316
  // Verify that input and output types are different
203
317
  type InputIsString = InputType extends { id: string } ? true : false;
204
318
  type OutputIsNumber = OutputType extends { id: number } ? true : false;
@@ -241,5 +355,13 @@ describe('createRoute with StandardSchema', () => {
241
355
 
242
356
  // Should accept without animation parameter (default will be used)
243
357
  navigation.navigate('/test-with-defaults', {});
358
+
359
+ const setParams = RouteWithDefaults.useSetParams();
360
+ setParams({});
361
+ setParams({ animation: false });
362
+
363
+ const replaceParams = RouteWithDefaults.useReplaceParams();
364
+ replaceParams({});
365
+ replaceParams({ animation: false });
244
366
  });
245
367
  });
@@ -8,7 +8,7 @@ import {
8
8
  NativeStackNavigationProp,
9
9
  } from '@granite-js/native/@react-navigation/native-stack';
10
10
  import type { StandardSchemaV1 } from '@standard-schema/spec';
11
- import { useMemo } from 'react';
11
+ import { useCallback, useMemo } from 'react';
12
12
  import { RESERVED_PATHS } from './constants';
13
13
  import type { ErrorComponent } from './types';
14
14
  import { defaultParserParams } from './utils/defaultParserParams';
@@ -29,16 +29,20 @@ export interface RegisterScreenInput {}
29
29
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
30
30
  export interface RegisterScreen {}
31
31
 
32
+ type RegisteredParamList = keyof RegisterScreenInput extends never ? ParamListBase : RegisterScreenInput;
33
+
32
34
  export type NavigationProps = NativeStackNavigationProp<
33
35
  // @ts-expect-error - override type
34
- keyof RegisterScreenInput extends never ? ParamListBase : RegisterScreenInput
36
+ RegisteredParamList
35
37
  >;
36
38
 
37
39
  export function useNavigation() {
38
40
  return useNavigationNative<NavigationProps>();
39
41
  }
40
42
 
41
- export type RouteHooksOptions<TScreen extends keyof RegisterScreen> =
43
+ type RouteHookScreen = keyof RegisterScreenInput | keyof RegisterScreen;
44
+
45
+ export type RouteHooksOptions<TScreen extends RouteHookScreen> =
42
46
  | {
43
47
  from: TScreen;
44
48
  strict?: true;
@@ -58,7 +62,7 @@ export const routeMap = new Map<
58
62
  }
59
63
  >();
60
64
 
61
- export function useMatchOptions<TScreen extends keyof RegisterScreen>(options: RouteHooksOptions<TScreen>) {
65
+ export function useMatchOptions<TScreen extends RouteHookScreen>(options: RouteHooksOptions<TScreen>) {
62
66
  const route = useRoute();
63
67
  const from = 'from' in options ? options.from : (route.name as TScreen);
64
68
  const strict = 'from' in options ? true : options.strict;
@@ -72,11 +76,11 @@ export function useMatchOptions<TScreen extends keyof RegisterScreen>(options: R
72
76
  return null;
73
77
  }
74
78
 
75
- if (!(routeMap.has(from) || RESERVED_PATHS.includes(from))) {
79
+ if (!(routeMap.has(from as keyof RegisterScreenInput) || RESERVED_PATHS.includes(from as string))) {
76
80
  throw new Error(`Route '${from}' is not registered`);
77
81
  }
78
82
 
79
- const routeOptions = routeMap.get(from);
83
+ const routeOptions = routeMap.get(from as keyof RegisterScreenInput);
80
84
  return {
81
85
  ...routeOptions?.options,
82
86
  parserParams: routeOptions?.options.parserParams ?? defaultParserParams,
@@ -168,34 +172,69 @@ export function useParams<TScreen extends keyof RegisterScreen>(
168
172
  return params;
169
173
  }
170
174
 
175
+ type SetParamsFunction<TParams> = (params: TParams extends undefined ? undefined : Partial<TParams>) => void;
176
+ type ReplaceParamsFunction<TParams> = (params: TParams extends undefined ? undefined : TParams) => void;
177
+
178
+ type CreatedRoute<TInput, TOutput> = {
179
+ _path: keyof RegisterScreenInput;
180
+ useNavigation: typeof useNavigation;
181
+ useParams: () => TOutput;
182
+ useSetParams: () => SetParamsFunction<TInput>;
183
+ useReplaceParams: () => ReplaceParamsFunction<TInput>;
184
+ _inputType: TInput;
185
+ _outputType: TOutput;
186
+ };
187
+
188
+ export function useSetParams<TScreen extends keyof RegisterScreenInput>(options: {
189
+ from: TScreen;
190
+ strict?: true;
191
+ }): SetParamsFunction<RegisterScreenInput[TScreen]>;
192
+ export function useSetParams(options: { strict: false }): SetParamsFunction<Readonly<object | undefined>>;
193
+
194
+ export function useSetParams<TScreen extends keyof RegisterScreenInput>(
195
+ options: RouteHooksOptions<TScreen>
196
+ ): SetParamsFunction<RegisterScreenInput[TScreen]> | SetParamsFunction<Readonly<object | undefined>> {
197
+ useMatchOptions(options);
198
+ const navigation = useNavigationNative<NativeStackNavigationProp<ParamListBase>>();
199
+
200
+ return useCallback((params: never) => navigation.setParams(params), [navigation]) as
201
+ | SetParamsFunction<RegisterScreenInput[TScreen]>
202
+ | SetParamsFunction<Readonly<object | undefined>>;
203
+ }
204
+
205
+ export function useReplaceParams<TScreen extends keyof RegisterScreenInput>(options: {
206
+ from: TScreen;
207
+ strict?: true;
208
+ }): ReplaceParamsFunction<RegisterScreenInput[TScreen]>;
209
+ export function useReplaceParams(options: { strict: false }): ReplaceParamsFunction<Readonly<object | undefined>>;
210
+
211
+ export function useReplaceParams<TScreen extends keyof RegisterScreenInput>(
212
+ options: RouteHooksOptions<TScreen>
213
+ ): ReplaceParamsFunction<RegisterScreenInput[TScreen]> | ReplaceParamsFunction<Readonly<object | undefined>> {
214
+ useMatchOptions(options);
215
+ const navigation = useNavigationNative<NativeStackNavigationProp<ParamListBase>>();
216
+
217
+ return useCallback((params: never) => navigation.replaceParams(params), [navigation]) as
218
+ | ReplaceParamsFunction<RegisterScreenInput[TScreen]>
219
+ | ReplaceParamsFunction<Readonly<object | undefined>>;
220
+ }
221
+
171
222
  // Overload 1: StandardSchema pattern
172
223
  export function createRoute<TSchema extends StandardSchemaV1<any, any>>(
173
224
  path: keyof RegisterScreenInput,
174
225
  options: Omit<RouteOptions<any>, 'validateParams'> & {
175
226
  validateParams: TSchema;
176
227
  }
177
- ): {
178
- _path: keyof RegisterScreenInput;
179
- useNavigation: typeof useNavigation;
180
- useParams: () => InferOutput<TSchema>;
181
- _inputType: InferInput<TSchema>;
182
- _outputType: InferOutput<TSchema>;
183
- };
228
+ ): CreatedRoute<InferInput<TSchema>, InferOutput<TSchema>>;
184
229
 
185
230
  // Overload 2: Function pattern
186
231
  export function createRoute<T extends Readonly<object | undefined>>(
187
232
  path: keyof RegisterScreenInput,
188
233
  options: RouteOptions<T>
189
- ): {
190
- _path: keyof RegisterScreenInput;
191
- useNavigation: typeof useNavigation;
192
- useParams: () => T;
193
- _inputType: T;
194
- _outputType: T;
195
- };
234
+ ): CreatedRoute<T, T>;
196
235
 
197
236
  // Implementation
198
- export function createRoute(path: keyof RegisterScreenInput, options: RouteOptions<any>) {
237
+ export function createRoute(path: keyof RegisterScreenInput, options: RouteOptions<any>): CreatedRoute<any, any> {
199
238
  const { component, screenOptions, errorComponent, ...restOptions } = options;
200
239
  routeMap.set(path, {
201
240
  options: restOptions,
@@ -209,6 +248,8 @@ export function createRoute(path: keyof RegisterScreenInput, options: RouteOptio
209
248
  _path,
210
249
  useNavigation,
211
250
  useParams: () => useParams({ from: _path as keyof RegisterScreen, strict: true }),
251
+ useSetParams: () => useSetParams({ from: _path, strict: true }),
252
+ useReplaceParams: () => useReplaceParams({ from: _path, strict: true }),
212
253
 
213
254
  // These properties are only used for type inference in the generated router files
214
255
  // The actual values are never accessed at runtime