@leancodepl/utils 9.5.3 → 9.6.0

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/README.md CHANGED
@@ -28,13 +28,13 @@ Asserts that a value is not undefined. Throws an error if the value is undefined
28
28
  import { assertDefined } from "@leancodepl/utils"
29
29
 
30
30
  interface User {
31
- name: string
32
- email: string
31
+ name: string
32
+ email: string
33
33
  }
34
34
 
35
35
  function processUserAssert(user?: User) {
36
- assertDefined(user) // Throws if undefined, no return value
37
- return user.name // TypeScript knows user is defined
36
+ assertDefined(user) // Throws if undefined, no return value
37
+ return user.name // TypeScript knows user is defined
38
38
  }
39
39
  ```
40
40
 
@@ -53,13 +53,13 @@ Asserts that a value is not null. Throws an error if the value is null.
53
53
  import { assertNotNull } from "@leancodepl/utils"
54
54
 
55
55
  interface User {
56
- name: string
57
- email: string
56
+ name: string
57
+ email: string
58
58
  }
59
59
 
60
60
  function processData(data: string | null) {
61
- assertNotNull(data, "Data cannot be null")
62
- return data.toUpperCase() // TypeScript knows data is string
61
+ assertNotNull(data, "Data cannot be null")
62
+ return data.toUpperCase() // TypeScript knows data is string
63
63
  }
64
64
  ```
65
65
 
@@ -78,13 +78,13 @@ Asserts that a value is not null or undefined. Throws an error if the value is n
78
78
  import { assertNotEmpty } from "@leancodepl/utils"
79
79
 
80
80
  interface User {
81
- name: string
82
- email: string
81
+ name: string
82
+ email: string
83
83
  }
84
84
 
85
85
  function processValue(value: string | null | undefined) {
86
- assertNotEmpty(value, "Value is required")
87
- return value.length // TypeScript knows value is string
86
+ assertNotEmpty(value, "Value is required")
87
+ return value.length // TypeScript knows value is string
88
88
  }
89
89
  ```
90
90
 
@@ -105,13 +105,13 @@ Ensures that a value is defined, returning it if defined or throwing an error if
105
105
  import { ensureDefined } from "@leancodepl/utils"
106
106
 
107
107
  interface User {
108
- name: string
109
- email: string
108
+ name: string
109
+ email: string
110
110
  }
111
111
 
112
112
  function processUser(user?: User) {
113
- const definedUser = ensureDefined(user) // Returns User or throws
114
- return definedUser.name
113
+ const definedUser = ensureDefined(user) // Returns User or throws
114
+ return definedUser.name
115
115
  }
116
116
  ```
117
117
 
@@ -132,13 +132,13 @@ Ensures that a value is not null, returning it if not null or throwing an error
132
132
  import { ensureNotNull } from "@leancodepl/utils"
133
133
 
134
134
  interface User {
135
- name: string
136
- email: string
135
+ name: string
136
+ email: string
137
137
  }
138
138
 
139
139
  function processData(data: string | null) {
140
- const validData = ensureNotNull(data, "Data cannot be null")
141
- return validData.toUpperCase()
140
+ const validData = ensureNotNull(data, "Data cannot be null")
141
+ return validData.toUpperCase()
142
142
  }
143
143
  ```
144
144
 
@@ -159,13 +159,13 @@ Ensures that a value is not null or undefined, returning it if valid or throwing
159
159
  import { ensureNotEmpty } from "@leancodepl/utils"
160
160
 
161
161
  interface User {
162
- name: string
163
- email: string
162
+ name: string
163
+ email: string
164
164
  }
165
165
 
166
166
  function processValue(value: string | null | undefined) {
167
- const validValue = ensureNotEmpty(value, "Value is required")
168
- return validValue.length
167
+ const validValue = ensureNotEmpty(value, "Value is required")
168
+ return validValue.length
169
169
  }
170
170
  ```
171
171
 
@@ -463,3 +463,28 @@ function MyComponent() {
463
463
  );
464
464
  }
465
465
  ```
466
+
467
+ ### `useSyncState(state, onChange, compare)`
468
+
469
+ Synchronizes external state changes with a callback function, calling the callback only when state actually changes.
470
+
471
+ **Parameters:**
472
+
473
+ - `state: T` - The current state value to monitor for changes
474
+ - `onChange: (state: T) => void` - Callback function executed when state changes
475
+ - `compare?: (a: T, b: T) => boolean` - Optional comparison function to determine if state has changed (defaults to
476
+ strict equality)
477
+
478
+ **Usage:**
479
+
480
+ ```typescript
481
+ import { useSyncState } from "@leancodepl/utils";
482
+
483
+ function MyComponent({ externalValue }: { externalValue: string }) {
484
+ useSyncState(externalValue, (newValue) => {
485
+ console.log('Value changed to:', newValue);
486
+ });
487
+
488
+ return <div>{externalValue}</div>;
489
+ }
490
+ ```
package/index.cjs.js CHANGED
@@ -405,6 +405,33 @@ function useBoundRunInTask(block) {
405
405
  };
406
406
  }
407
407
 
408
+ /**
409
+ * Synchronizes external state changes with a callback function, calling the callback only when state actually changes.
410
+ *
411
+ * @param state - The current state value to monitor for changes
412
+ * @param onChange - Callback function executed when state changes
413
+ * @param compare - Optional comparison function to determine if state has changed (defaults to strict equality)
414
+ * @example
415
+ * ```typescript
416
+ * import { useSyncState } from "@leancodepl/utils";
417
+ *
418
+ * function MyComponent({ externalValue }: { externalValue: string }) {
419
+ * useSyncState(externalValue, (newValue) => {
420
+ * console.log('Value changed to:', newValue);
421
+ * });
422
+ *
423
+ * return <div>{externalValue}</div>;
424
+ * }
425
+ * ```
426
+ */ function useSyncState(state, onChange, compare) {
427
+ const [prevState, setPrevState] = react.useState(state);
428
+ const hasChanged = compare ? !compare(state, prevState) : state !== prevState;
429
+ if (hasChanged) {
430
+ setPrevState(state);
431
+ onChange(state);
432
+ }
433
+ }
434
+
408
435
  exports.addPrefix = addPrefix;
409
436
  exports.assertDefined = assertDefined;
410
437
  exports.assertNotEmpty = assertNotEmpty;
@@ -422,3 +449,4 @@ exports.useDialog = useDialog;
422
449
  exports.useKeyByRoute = useKeyByRoute;
423
450
  exports.useRunInTask = useRunInTask;
424
451
  exports.useSetUnset = useSetUnset;
452
+ exports.useSyncState = useSyncState;
package/index.esm.js CHANGED
@@ -403,4 +403,31 @@ function useBoundRunInTask(block) {
403
403
  };
404
404
  }
405
405
 
406
- export { addPrefix, assertDefined, assertNotEmpty, assertNotNull, capitalizeDeep, downloadFile, ensureDefined, ensureNotEmpty, ensureNotNull, toLowerFirst, toUpperFirst, uncapitalizeDeep, useBoundRunInTask, useDialog, useKeyByRoute, useRunInTask, useSetUnset };
406
+ /**
407
+ * Synchronizes external state changes with a callback function, calling the callback only when state actually changes.
408
+ *
409
+ * @param state - The current state value to monitor for changes
410
+ * @param onChange - Callback function executed when state changes
411
+ * @param compare - Optional comparison function to determine if state has changed (defaults to strict equality)
412
+ * @example
413
+ * ```typescript
414
+ * import { useSyncState } from "@leancodepl/utils";
415
+ *
416
+ * function MyComponent({ externalValue }: { externalValue: string }) {
417
+ * useSyncState(externalValue, (newValue) => {
418
+ * console.log('Value changed to:', newValue);
419
+ * });
420
+ *
421
+ * return <div>{externalValue}</div>;
422
+ * }
423
+ * ```
424
+ */ function useSyncState(state, onChange, compare) {
425
+ const [prevState, setPrevState] = useState(state);
426
+ const hasChanged = compare ? !compare(state, prevState) : state !== prevState;
427
+ if (hasChanged) {
428
+ setPrevState(state);
429
+ onChange(state);
430
+ }
431
+ }
432
+
433
+ export { addPrefix, assertDefined, assertNotEmpty, assertNotNull, capitalizeDeep, downloadFile, ensureDefined, ensureNotEmpty, ensureNotNull, toLowerFirst, toUpperFirst, uncapitalizeDeep, useBoundRunInTask, useDialog, useKeyByRoute, useRunInTask, useSetUnset, useSyncState };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leancodepl/utils",
3
- "version": "9.5.3",
3
+ "version": "9.6.0",
4
4
  "license": "Apache-2.0",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -10,7 +10,7 @@
10
10
  "node": ">=18.0.0"
11
11
  },
12
12
  "dependencies": {
13
- "@leancodepl/api-date": "9.5.3",
13
+ "@leancodepl/api-date": "9.6.0",
14
14
  "tiny-invariant": ">=1.3.1"
15
15
  },
16
16
  "peerDependencies": {
package/src/index.d.ts CHANGED
@@ -14,3 +14,4 @@ export * from "./lib/hooks/useBoundRunInTask";
14
14
  export * from "./lib/hooks/useKeyByRoute";
15
15
  export * from "./lib/hooks/useSetUnset";
16
16
  export * from "./lib/hooks/useDialog";
17
+ export * from "./lib/hooks/useSyncState";
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Synchronizes external state changes with a callback function, calling the callback only when state actually changes.
3
+ *
4
+ * @param state - The current state value to monitor for changes
5
+ * @param onChange - Callback function executed when state changes
6
+ * @param compare - Optional comparison function to determine if state has changed (defaults to strict equality)
7
+ * @example
8
+ * ```typescript
9
+ * import { useSyncState } from "@leancodepl/utils";
10
+ *
11
+ * function MyComponent({ externalValue }: { externalValue: string }) {
12
+ * useSyncState(externalValue, (newValue) => {
13
+ * console.log('Value changed to:', newValue);
14
+ * });
15
+ *
16
+ * return <div>{externalValue}</div>;
17
+ * }
18
+ * ```
19
+ */
20
+ export declare function useSyncState<T>(state: T, onChange: (state: T) => void, compare?: (a: T, b: T) => boolean): void;