react-native-reanimated 4.3.0-rc.0 → 4.3.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.
Files changed (153) hide show
  1. package/Common/cpp/reanimated/AnimatedSensor/AnimatedSensorModule.cpp +8 -5
  2. package/Common/cpp/reanimated/AnimatedSensor/AnimatedSensorModule.h +3 -5
  3. package/Common/cpp/reanimated/CSS/common/values/CSSValueVariant.cpp +2 -3
  4. package/Common/cpp/reanimated/CSS/interpolation/PropertyInterpolator.cpp +2 -3
  5. package/Common/cpp/reanimated/CSS/utils/DelayedItemsManager.cpp +2 -2
  6. package/Common/cpp/reanimated/CSS/utils/DelayedItemsManager.h +2 -2
  7. package/Common/cpp/reanimated/Compat/WorkletsApi.h +13 -0
  8. package/Common/cpp/reanimated/Events/UIEventHandler.cpp +1 -1
  9. package/Common/cpp/reanimated/Events/UIEventHandler.h +3 -4
  10. package/Common/cpp/reanimated/Events/UIEventHandlerRegistry.cpp +3 -2
  11. package/Common/cpp/reanimated/Events/UIEventHandlerRegistry.h +1 -1
  12. package/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.cpp +2 -8
  13. package/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.h +2 -6
  14. package/Common/cpp/reanimated/Fabric/ReanimatedMountHook.cpp +1 -6
  15. package/Common/cpp/reanimated/Fabric/ReanimatedMountHook.h +1 -8
  16. package/Common/cpp/reanimated/Fabric/updates/AnimatedPropsRegistry.cpp +0 -2
  17. package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsManager.h +1 -2
  18. package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxyCommon.h +1 -1
  19. package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.cpp +9 -9
  20. package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Experimental.h +2 -3
  21. package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.cpp +21 -21
  22. package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsProxy_Legacy.h +3 -3
  23. package/Common/cpp/reanimated/LayoutAnimations/LayoutAnimationsUtils.h +3 -3
  24. package/Common/cpp/reanimated/LayoutAnimations/SharedTransitions.cpp +24 -20
  25. package/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +25 -25
  26. package/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h +1 -2
  27. package/Common/cpp/reanimated/RuntimeDecorators/UIRuntimeDecorator.cpp +22 -22
  28. package/Common/cpp/reanimated/RuntimeDecorators/UIRuntimeDecorator.h +10 -10
  29. package/Common/cpp/reanimated/Tools/ReaJSIUtils.h +5 -5
  30. package/android/build.gradle +0 -21
  31. package/android/src/main/cpp/reanimated/android/NativeProxy.cpp +6 -10
  32. package/android/src/main/cpp/reanimated/android/NativeProxy.h +1 -2
  33. package/android/src/main/java/com/swmansion/reanimated/CopiedEvent.java +66 -9
  34. package/android/src/main/java/com/swmansion/reanimated/NodesManager.java +16 -11
  35. package/android/src/main/java/com/swmansion/reanimated/nativeProxy/EventHandler.java +34 -8
  36. package/android/src/main/java/com/swmansion/reanimated/nativeProxy/NoopEventHandler.java +27 -6
  37. package/apple/reanimated/apple/REANodesManager.mm +0 -8
  38. package/apple/reanimated/apple/ReanimatedModule.mm +6 -12
  39. package/apple/reanimated/apple/native/NativeProxy.h +1 -2
  40. package/apple/reanimated/apple/native/NativeProxy.mm +1 -1
  41. package/compatibility.json +7 -29
  42. package/lib/module/PropsRegistryGarbageCollector.js +3 -0
  43. package/lib/module/PropsRegistryGarbageCollector.js.map +1 -1
  44. package/lib/module/common/style/processors/colors.js +31 -4
  45. package/lib/module/common/style/processors/colors.js.map +1 -1
  46. package/lib/module/common/style/processors/filter.js +103 -61
  47. package/lib/module/common/style/processors/filter.js.map +1 -1
  48. package/lib/module/common/style/processors/font.js +7 -1
  49. package/lib/module/common/style/processors/font.js.map +1 -1
  50. package/lib/module/common/style/processors/insets.js +26 -14
  51. package/lib/module/common/style/processors/insets.js.map +1 -1
  52. package/lib/module/common/style/processors/others.js +15 -5
  53. package/lib/module/common/style/processors/others.js.map +1 -1
  54. package/lib/module/common/style/processors/shadows.js +14 -3
  55. package/lib/module/common/style/processors/shadows.js.map +1 -1
  56. package/lib/module/common/style/processors/transform.js +30 -1
  57. package/lib/module/common/style/processors/transform.js.map +1 -1
  58. package/lib/module/common/style/processors/transformOrigin.js +20 -3
  59. package/lib/module/common/style/processors/transformOrigin.js.map +1 -1
  60. package/lib/module/common/utils/guards.js +27 -7
  61. package/lib/module/common/utils/guards.js.map +1 -1
  62. package/lib/module/common/utils/parsers.js +2 -1
  63. package/lib/module/common/utils/parsers.js.map +1 -1
  64. package/lib/module/css/native/managers/CSSManager.js +5 -13
  65. package/lib/module/css/native/managers/CSSManager.js.map +1 -1
  66. package/lib/module/css/native/managers/CSSTransitionsManager.js +47 -44
  67. package/lib/module/css/native/managers/CSSTransitionsManager.js.map +1 -1
  68. package/lib/module/css/native/normalization/transition/config.js +35 -17
  69. package/lib/module/css/native/normalization/transition/config.js.map +1 -1
  70. package/lib/module/featureFlags/index.js +1 -1
  71. package/lib/module/featureFlags/staticFlags.json +1 -1
  72. package/lib/module/hook/useAnimatedStyle.js +7 -1
  73. package/lib/module/hook/useAnimatedStyle.js.map +1 -1
  74. package/lib/module/hook/useHandler.js +82 -28
  75. package/lib/module/hook/useHandler.js.map +1 -1
  76. package/lib/module/hook/utils.js +1 -74
  77. package/lib/module/hook/utils.js.map +1 -1
  78. package/lib/module/initializers.js +2 -0
  79. package/lib/module/initializers.js.map +1 -1
  80. package/lib/module/jestUtils/common.js +10 -0
  81. package/lib/module/jestUtils/common.js.map +1 -1
  82. package/lib/module/jestUtils/index.js +2 -5
  83. package/lib/module/jestUtils/index.js.map +1 -1
  84. package/lib/module/jestUtils/index.web.js +1 -1
  85. package/lib/module/jestUtils/index.web.js.map +1 -1
  86. package/lib/module/platform-specific/jsVersion.js +1 -1
  87. package/lib/module/platform-specific/jsVersion.js.map +1 -1
  88. package/lib/typescript/PropsRegistryGarbageCollector.d.ts.map +1 -1
  89. package/lib/typescript/common/style/processors/colors.d.ts +3 -3
  90. package/lib/typescript/common/style/processors/colors.d.ts.map +1 -1
  91. package/lib/typescript/common/style/processors/filter.d.ts +0 -4
  92. package/lib/typescript/common/style/processors/filter.d.ts.map +1 -1
  93. package/lib/typescript/common/style/processors/font.d.ts +1 -1
  94. package/lib/typescript/common/style/processors/font.d.ts.map +1 -1
  95. package/lib/typescript/common/style/processors/insets.d.ts.map +1 -1
  96. package/lib/typescript/common/style/processors/others.d.ts +1 -1
  97. package/lib/typescript/common/style/processors/others.d.ts.map +1 -1
  98. package/lib/typescript/common/style/processors/shadows.d.ts.map +1 -1
  99. package/lib/typescript/common/style/processors/transform.d.ts.map +1 -1
  100. package/lib/typescript/common/style/processors/transformOrigin.d.ts +2 -2
  101. package/lib/typescript/common/style/processors/transformOrigin.d.ts.map +1 -1
  102. package/lib/typescript/common/utils/guards.d.ts +0 -1
  103. package/lib/typescript/common/utils/guards.d.ts.map +1 -1
  104. package/lib/typescript/common/utils/parsers.d.ts.map +1 -1
  105. package/lib/typescript/commonTypes.d.ts +1 -1
  106. package/lib/typescript/commonTypes.d.ts.map +1 -1
  107. package/lib/typescript/css/native/managers/CSSManager.d.ts +0 -1
  108. package/lib/typescript/css/native/managers/CSSManager.d.ts.map +1 -1
  109. package/lib/typescript/css/native/managers/CSSTransitionsManager.d.ts +5 -4
  110. package/lib/typescript/css/native/managers/CSSTransitionsManager.d.ts.map +1 -1
  111. package/lib/typescript/css/native/normalization/transition/config.d.ts.map +1 -1
  112. package/lib/typescript/css/native/types/transition.d.ts +15 -1
  113. package/lib/typescript/css/native/types/transition.d.ts.map +1 -1
  114. package/lib/typescript/featureFlags/index.d.ts +1 -1
  115. package/lib/typescript/hook/useAnimatedStyle.d.ts.map +1 -1
  116. package/lib/typescript/hook/useHandler.d.ts +8 -9
  117. package/lib/typescript/hook/useHandler.d.ts.map +1 -1
  118. package/lib/typescript/hook/utils.d.ts +0 -5
  119. package/lib/typescript/hook/utils.d.ts.map +1 -1
  120. package/lib/typescript/jestUtils/common.d.ts +2 -0
  121. package/lib/typescript/jestUtils/common.d.ts.map +1 -1
  122. package/lib/typescript/jestUtils/index.d.ts +4 -3
  123. package/lib/typescript/jestUtils/index.d.ts.map +1 -1
  124. package/lib/typescript/platform-specific/jsVersion.d.ts +1 -1
  125. package/lib/typescript/platform-specific/jsVersion.d.ts.map +1 -1
  126. package/package.json +10 -9
  127. package/src/PropsRegistryGarbageCollector.ts +3 -0
  128. package/src/common/style/processors/colors.ts +20 -7
  129. package/src/common/style/processors/filter.ts +95 -70
  130. package/src/common/style/processors/font.ts +5 -2
  131. package/src/common/style/processors/insets.ts +23 -14
  132. package/src/common/style/processors/others.ts +12 -6
  133. package/src/common/style/processors/shadows.ts +10 -6
  134. package/src/common/style/processors/transform.ts +15 -1
  135. package/src/common/style/processors/transformOrigin.ts +40 -11
  136. package/src/common/utils/guards.ts +21 -16
  137. package/src/common/utils/parsers.ts +1 -1
  138. package/src/commonTypes.ts +1 -1
  139. package/src/css/native/managers/CSSManager.ts +8 -14
  140. package/src/css/native/managers/CSSTransitionsManager.ts +52 -54
  141. package/src/css/native/normalization/transition/config.ts +35 -27
  142. package/src/css/native/types/transition.ts +15 -1
  143. package/src/featureFlags/index.ts +1 -1
  144. package/src/featureFlags/staticFlags.json +1 -1
  145. package/src/hook/useAnimatedStyle.ts +15 -6
  146. package/src/hook/useHandler.ts +150 -64
  147. package/src/hook/utils.ts +1 -127
  148. package/src/initializers.ts +1 -0
  149. package/src/jestUtils/common.ts +10 -0
  150. package/src/jestUtils/index.ts +5 -8
  151. package/src/jestUtils/index.web.ts +1 -1
  152. package/src/platform-specific/jsVersion.ts +1 -1
  153. package/src/privateGlobals.d.ts +4 -0
@@ -79,9 +79,10 @@ export function normalizeCSSTransitionProperties(
79
79
  transitionDelay,
80
80
  transitionBehavior,
81
81
  } = expandedProperties;
82
- const specificProperties: string[] = [];
83
82
  let allPropertiesTransition = false;
84
83
  const settings: Record<string, NormalizedSingleCSSTransitionSettings> = {};
84
+ const specificProperties = new Set<string>();
85
+ const processedProperties = new Set<string>();
85
86
 
86
87
  if (!transitionProperty.length) {
87
88
  // For cases when transition property hasn't been explicitly specified
@@ -95,42 +96,49 @@ export function normalizeCSSTransitionProperties(
95
96
  // occurrence and ignore remaining ones)
96
97
  for (let i = transitionProperty.length - 1; i >= 0; i--) {
97
98
  const property = transitionProperty[i];
98
- // Continue if there was a prop with the same name specified later
99
- // (we don't want to override the last occurrence of the property)
100
- if (settings?.[property]) {
99
+ // We always respect the last occurrence of a property, even if it gets
100
+ // pruned as inactive. That means earlier occurrences must be ignored.
101
+ if (processedProperties.has(property)) {
101
102
  continue;
102
103
  }
104
+ processedProperties.add(property);
103
105
 
104
- if (property === 'all') {
105
- allPropertiesTransition = true;
106
- } else {
107
- specificProperties.push(property);
106
+ const duration = normalizeDuration(
107
+ transitionDuration[i % transitionDuration.length]
108
+ );
109
+ const delay = normalizeDelay(transitionDelay[i % transitionDelay.length]);
110
+
111
+ // Skip if effective duration is 0 (the transition would be immediate so there is no need
112
+ // to apply it and we can just treat it as a plain render without a transition)
113
+ if (duration + delay <= 0) {
114
+ continue;
108
115
  }
109
116
 
110
- settings[property] = {
111
- duration: normalizeDuration(
112
- transitionDuration[i % transitionDuration.length]
113
- ),
114
- timingFunction: normalizeTimingFunction(
115
- transitionTimingFunction[i % transitionTimingFunction.length]
116
- ),
117
- delay: normalizeDelay(transitionDelay[i % transitionDelay.length]),
118
- allowDiscrete: normalizeTransitionBehavior(
119
- transitionBehavior[i % transitionBehavior.length]
120
- ),
121
- };
117
+ const timingFunction = normalizeTimingFunction(
118
+ transitionTimingFunction[i % transitionTimingFunction.length]
119
+ );
120
+ const allowDiscrete = normalizeTransitionBehavior(
121
+ transitionBehavior[i % transitionBehavior.length]
122
+ );
123
+
124
+ settings[property] = { duration, timingFunction, delay, allowDiscrete };
122
125
 
123
126
  // 'all' transition property overrides all properties before it,
124
127
  // so we don't need to process them
125
- if (allPropertiesTransition) {
128
+ if (property === 'all') {
129
+ allPropertiesTransition = true;
126
130
  break;
127
131
  }
132
+
133
+ specificProperties.add(property);
134
+ }
135
+
136
+ if (allPropertiesTransition) {
137
+ return { specificProperties: undefined, settings };
138
+ }
139
+ if (specificProperties.size) {
140
+ return { specificProperties, settings };
128
141
  }
129
142
 
130
- return {
131
- properties: allPropertiesTransition
132
- ? undefined
133
- : specificProperties.reverse(),
134
- settings,
135
- };
143
+ return null;
136
144
  }
@@ -13,7 +13,21 @@ export type CSSTransitionConfig = Record<
13
13
  (NormalizedSingleCSSTransitionSettings & { value: [unknown, unknown] }) | null
14
14
  >;
15
15
 
16
+ /**
17
+ * `specificProperties`
18
+ *
19
+ * - `undefined`: accept all props (equivalent to `transition-property: all`)
20
+ * - `Set<string>`: accept only props present in the set
21
+ *
22
+ * `settings`
23
+ *
24
+ * - Keys are prop names, plus an optional `all` fallback key
25
+ * - How to read it for a prop `propName`:
26
+ *
27
+ * - Primary: `settings[propName]` (if present)
28
+ * - Fallback: `settings.all` (otherwise)
29
+ */
16
30
  export type NormalizedCSSTransitionConfig = {
17
- properties: string[] | undefined;
31
+ specificProperties: Set<string> | undefined;
18
32
  settings: Record<string, NormalizedSingleCSSTransitionSettings>;
19
33
  };
@@ -80,7 +80,7 @@ const DefaultStaticFeatureFlags = {
80
80
  USE_SYNCHRONIZABLE_FOR_MUTABLES: true,
81
81
  USE_COMMIT_HOOK_ONLY_FOR_REACT_COMMITS: true,
82
82
  ENABLE_SHARED_ELEMENT_TRANSITIONS: false,
83
- FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS: false,
83
+ FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS: true,
84
84
  } as const satisfies typeof StaticFeatureFlagsJSON;
85
85
 
86
86
  type StaticFeatureFlagsSchema = {
@@ -7,5 +7,5 @@
7
7
  "USE_SYNCHRONIZABLE_FOR_MUTABLES": true,
8
8
  "USE_COMMIT_HOOK_ONLY_FOR_REACT_COMMITS": true,
9
9
  "ENABLE_SHARED_ELEMENT_TRANSITIONS": false,
10
- "FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS": false
10
+ "FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS": true
11
11
  }
@@ -30,12 +30,7 @@ import type {
30
30
  JestAnimatedStyleHandle,
31
31
  } from './commonTypes';
32
32
  import { useSharedValue } from './useSharedValue';
33
- import {
34
- buildWorkletsHash,
35
- isAnimated,
36
- shallowEqual,
37
- validateAnimatedStyles,
38
- } from './utils';
33
+ import { isAnimated, shallowEqual, validateAnimatedStyles } from './utils';
39
34
 
40
35
  interface AnimatedState {
41
36
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -450,6 +445,20 @@ function checkSharedValueUsage(
450
445
  }
451
446
  }
452
447
 
448
+ // Builds one big hash from multiple worklets' hashes.
449
+ function buildWorkletsHash<Args extends unknown[], ReturnValue>(
450
+ worklets:
451
+ | Record<string, WorkletFunction<Args, ReturnValue>>
452
+ | WorkletFunction<Args, ReturnValue>[]
453
+ ) {
454
+ // For arrays `Object.values` returns the array itself.
455
+ return Object.values(worklets).reduce(
456
+ (acc, worklet: WorkletFunction<Args, ReturnValue>) =>
457
+ acc + worklet.__workletHash.toString(),
458
+ ''
459
+ );
460
+ }
461
+
453
462
  /**
454
463
  * Lets you create a styles object, similar to StyleSheet styles, which can be
455
464
  * animated using shared values.
@@ -1,43 +1,129 @@
1
1
  'use strict';
2
2
  import { useEffect, useRef } from 'react';
3
3
  import type { WorkletFunction } from 'react-native-worklets';
4
- import { makeShareable } from 'react-native-worklets';
4
+ import { isWorkletFunction, makeShareable } from 'react-native-worklets';
5
+ import type { WorkletClosure } from 'react-native-worklets/lib/typescript/types';
5
6
 
6
- import { IS_JEST, IS_WEB } from '../common';
7
+ import type { UnknownRecord } from '../common';
8
+ import { IS_WEB, ReanimatedError } from '../common';
7
9
  import type { DependencyList, ReanimatedEvent } from './commonTypes';
8
- import { areDependenciesEqual, buildDependencies } from './utils';
9
10
 
10
11
  interface GeneralHandler<
11
- Event extends object,
12
- Context extends Record<string, unknown>,
12
+ TEvent extends object,
13
+ TContext extends UnknownRecord,
13
14
  > {
14
- (event: ReanimatedEvent<Event>, context: Context): void;
15
+ (event: ReanimatedEvent<TEvent>, context: TContext): void;
15
16
  }
16
17
 
17
- type GeneralWorkletHandler<
18
- Event extends object,
19
- Context extends Record<string, unknown>,
20
- > = WorkletFunction<[event: ReanimatedEvent<Event>, context: Context]>;
21
-
22
18
  type GeneralHandlers<
23
- Event extends object,
24
- Context extends Record<string, unknown>,
25
- > = Record<string, GeneralHandler<Event, Context> | undefined>;
26
-
27
- type GeneralWorkletHandlers<
28
- Event extends object,
29
- Context extends Record<string, unknown>,
30
- > = Record<string, GeneralWorkletHandler<Event, Context>>;
31
-
32
- interface ContextWithDependencies<Context extends Record<string, unknown>> {
33
- context: Context;
34
- savedDependencies: DependencyList;
35
- }
19
+ TEvent extends object,
20
+ TContext extends UnknownRecord,
21
+ > = Record<string, GeneralHandler<TEvent, TContext> | undefined>;
36
22
 
37
- export interface UseHandlerContext<Context extends Record<string, unknown>> {
38
- context: Context;
23
+ export interface UseHandlerContext<TContext extends UnknownRecord> {
24
+ context: TContext;
39
25
  doDependenciesDiffer: boolean;
40
- useWeb: boolean;
26
+ }
27
+
28
+ function isBabelPluginEnabled(handlers: UnknownRecord): boolean {
29
+ if (!IS_WEB) {
30
+ // Babel plugin must be enabled in all non-web environments.
31
+ return true;
32
+ }
33
+
34
+ const handlerFunctions = Object.values(handlers);
35
+ // If there is no function provided, we assume that the Babel plugin is enabled.
36
+ return (
37
+ handlerFunctions.length === 0 || handlerFunctions.some(isWorkletFunction)
38
+ );
39
+ }
40
+
41
+ function ensureWorkletHandlers(handlers: UnknownRecord) {
42
+ const nonWorkletNames = Object.entries(handlers).reduce<string[]>(
43
+ (acc, [name, handler]) => {
44
+ if (!isWorkletFunction(handler)) acc.push(name);
45
+ return acc;
46
+ },
47
+ []
48
+ );
49
+
50
+ if (nonWorkletNames.length > 0) {
51
+ throw new ReanimatedError(
52
+ `Passed handlers that are not worklets. Only worklet functions are allowed. Handlers "${nonWorkletNames.join(', ')}" are not worklets.`
53
+ );
54
+ }
55
+ }
56
+
57
+ const objectIs: (a: unknown, b: unknown) => boolean =
58
+ typeof Object.is === 'function'
59
+ ? Object.is
60
+ : (x, y) =>
61
+ (x === y && (x !== 0 || 1 / (x as number) === 1 / (y as number))) ||
62
+ (Number.isNaN(x as number) && Number.isNaN(y as number));
63
+
64
+ function areWorkletClosuresEqual(
65
+ next: WorkletClosure,
66
+ prev: WorkletClosure
67
+ ): boolean {
68
+ const nextKeys = Object.keys(next);
69
+ const prevKeys = Object.keys(prev);
70
+
71
+ return (
72
+ prevKeys.length === nextKeys.length &&
73
+ prevKeys.every((key) => key in next && objectIs(next[key], prev[key]))
74
+ );
75
+ }
76
+
77
+ function areWorkletsEqual(
78
+ next: WorkletFunction,
79
+ prev: WorkletFunction
80
+ ): boolean {
81
+ if (objectIs(next, prev)) {
82
+ return true;
83
+ }
84
+
85
+ return (
86
+ next.__workletHash === prev.__workletHash &&
87
+ areWorkletClosuresEqual(next.__closure, prev.__closure)
88
+ );
89
+ }
90
+
91
+ function areWorkletHandlersEqual(
92
+ next: Partial<Record<string, WorkletFunction>> | undefined,
93
+ prev: Partial<Record<string, WorkletFunction>> | undefined
94
+ ) {
95
+ if (!next || !prev) {
96
+ return false;
97
+ }
98
+
99
+ const nextKeys = Object.keys(next);
100
+ const prevKeys = Object.keys(prev);
101
+
102
+ if (nextKeys.length !== prevKeys.length) {
103
+ return false;
104
+ }
105
+
106
+ return nextKeys.every((key) => {
107
+ const nextValue = next[key];
108
+ const prevValue = prev[key];
109
+
110
+ if (!nextValue || !prevValue) {
111
+ return false;
112
+ }
113
+
114
+ return areWorkletsEqual(nextValue, prevValue);
115
+ });
116
+ }
117
+
118
+ function areDependenciesEqual(
119
+ next: Array<unknown> | undefined,
120
+ prev: Array<unknown> | undefined
121
+ ): boolean {
122
+ if (!next || !prev || next.length !== prev.length) {
123
+ return false;
124
+ }
125
+
126
+ return next.every((value, index) => objectIs(value, prev[index]));
41
127
  }
42
128
 
43
129
  /**
@@ -46,52 +132,52 @@ export interface UseHandlerContext<Context extends Record<string, unknown>> {
46
132
  * @param handlers - An object of event handlers.
47
133
  * @param dependencies - An optional array of dependencies.
48
134
  * @returns An object containing a boolean indicating whether the dependencies
49
- * have changed, and a boolean indicating whether the code is running on the
50
- * web.
135
+ * have changed.
51
136
  * @see https://docs.swmansion.com/react-native-reanimated/docs/advanced/useHandler
52
137
  */
53
- // @ts-expect-error This overload is required by our API.
54
- export function useHandler<
55
- Event extends object,
56
- Context extends Record<string, unknown>,
57
- >(
138
+ export function useHandler<Event extends object, Context extends UnknownRecord>(
58
139
  handlers: GeneralHandlers<Event, Context>,
59
140
  dependencies?: DependencyList
60
- ): UseHandlerContext<Context>;
61
-
62
- export function useHandler<
63
- Event extends object,
64
- Context extends Record<string, unknown>,
65
- >(
66
- handlers: GeneralWorkletHandlers<Event, Context>,
67
- dependencies?: DependencyList
68
141
  ): UseHandlerContext<Context> {
69
- const initRef = useRef<ContextWithDependencies<Context> | null>(null);
70
- if (initRef.current === null) {
71
- const context = makeShareable({} as Context);
72
- initRef.current = {
73
- context,
74
- savedDependencies: [],
75
- };
76
- }
142
+ 'use no memo';
77
143
 
78
- useEffect(() => {
79
- return () => {
80
- initRef.current = null;
144
+ const stateRef = useRef<{
145
+ context: Context;
146
+ prevHandlers: GeneralHandlers<Event, Context> | undefined;
147
+ prevDependencies: DependencyList;
148
+ } | null>(null);
149
+
150
+ if (stateRef.current === null) {
151
+ stateRef.current = {
152
+ context: makeShareable({} as Context),
153
+ prevHandlers: undefined,
154
+ prevDependencies: [],
81
155
  };
82
- }, []);
156
+ }
83
157
 
84
- const { context, savedDependencies } = initRef.current;
158
+ const state = stateRef.current;
159
+ let doDependenciesDiffer = true;
85
160
 
86
- dependencies = buildDependencies(
87
- dependencies,
88
- handlers as Record<string, WorkletFunction>
89
- );
161
+ if (isBabelPluginEnabled(handlers)) {
162
+ if (__DEV__) {
163
+ ensureWorkletHandlers(handlers);
164
+ }
165
+ doDependenciesDiffer = !areWorkletHandlersEqual(
166
+ handlers as Record<string, WorkletFunction>,
167
+ state.prevHandlers as Record<string, WorkletFunction>
168
+ );
169
+ } else if (dependencies) {
170
+ doDependenciesDiffer = !areDependenciesEqual(
171
+ dependencies,
172
+ state.prevDependencies
173
+ );
174
+ }
90
175
 
91
- const doDependenciesDiffer =
92
- !areDependenciesEqual(dependencies, savedDependencies) || !dependencies;
93
- initRef.current.savedDependencies = dependencies;
94
- const useWeb = IS_WEB || IS_JEST;
176
+ // Write after commit to avoid corruption from interrupted renders (in case of concurrent mode).
177
+ useEffect(() => {
178
+ state.prevHandlers = handlers;
179
+ state.prevDependencies = dependencies;
180
+ });
95
181
 
96
- return { context, doDependenciesDiffer, useWeb };
182
+ return { context: state.context, doDependenciesDiffer };
97
183
  }
package/src/hook/utils.ts CHANGED
@@ -1,131 +1,5 @@
1
1
  'use strict';
2
- import type { WorkletFunction } from 'react-native-worklets';
3
- import { isWorkletFunction } from 'react-native-worklets';
4
-
5
- import { IS_WEB, logger, ReanimatedError } from '../common';
6
- import type { DependencyList } from './commonTypes';
7
-
8
- // Builds one big hash from multiple worklets' hashes.
9
- export function buildWorkletsHash<Args extends unknown[], ReturnValue>(
10
- worklets:
11
- | Record<string, WorkletFunction<Args, ReturnValue>>
12
- | WorkletFunction<Args, ReturnValue>[]
13
- ) {
14
- // For arrays `Object.values` returns the array itself.
15
- return Object.values(worklets).reduce(
16
- (acc, worklet: WorkletFunction<Args, ReturnValue>) =>
17
- acc + worklet.__workletHash.toString(),
18
- ''
19
- );
20
- }
21
-
22
- // Builds dependencies array for useEvent handlers.
23
- export function buildDependencies(
24
- dependencies: DependencyList | undefined,
25
- handlers: Record<string, WorkletFunction>
26
- ) {
27
- const result = dependencies ?? [];
28
-
29
- const nonWorkletHandlerNames = Object.entries(handlers).reduce<string[]>(
30
- (acc, [name, handler]) => {
31
- if (!isWorkletFunction(handler)) {
32
- acc.push(name);
33
- }
34
- return acc;
35
- },
36
- []
37
- );
38
-
39
- if (nonWorkletHandlerNames.length === 0) {
40
- result.push(buildWorkletsHash(handlers));
41
- return result;
42
- }
43
-
44
- if (!__DEV__) {
45
- return result;
46
- }
47
-
48
- const handlerNames = nonWorkletHandlerNames.join(', ');
49
-
50
- // On native, only worklets are allowed
51
- if (!IS_WEB) {
52
- throw new ReanimatedError(
53
- `Passed handlers that are not worklets. Only worklet functions are allowed. Handlers "${handlerNames}" are not worklets.`
54
- );
55
- }
56
-
57
- // On web, non-worklets are allowed only when dependencies are provided
58
- if (__DEV__ && !dependencies) {
59
- logger.warn(
60
- `Non-worklet handlers ("${handlerNames}") were passed without a dependency array. This will cause the hook to update on every render. Please provide a dependency array or use only worklet functions instead.`
61
- );
62
- return undefined;
63
- }
64
-
65
- return result;
66
- }
67
-
68
- function areWorkletsEqual(
69
- worklet1: WorkletFunction,
70
- worklet2: WorkletFunction
71
- ) {
72
- if (worklet1.__workletHash === worklet2.__workletHash) {
73
- const closure1Keys = Object.keys(worklet1.__closure);
74
- const closure2Keys = Object.keys(worklet2.__closure);
75
-
76
- return (
77
- closure1Keys.length === closure2Keys.length &&
78
- closure1Keys.every(
79
- (key) =>
80
- key in worklet2.__closure &&
81
- worklet1.__closure[key] === worklet2.__closure[key]
82
- )
83
- );
84
- }
85
-
86
- return false;
87
- }
88
-
89
- // This is supposed to work as useEffect comparison.
90
- export function areDependenciesEqual(
91
- nextDependencies: DependencyList,
92
- prevDependencies: DependencyList
93
- ) {
94
- function is(x: number, y: number) {
95
- return (
96
- (x === y && (x !== 0 || 1 / x === 1 / y)) ||
97
- (Number.isNaN(x) && Number.isNaN(y))
98
- );
99
- }
100
- const objectIs: (nextDeps: unknown, prevDeps: unknown) => boolean =
101
- typeof Object.is === 'function' ? Object.is : is;
102
-
103
- function areHookInputsEqual(
104
- nextDeps: DependencyList,
105
- prevDeps: DependencyList
106
- ) {
107
- if (!nextDeps || !prevDeps || prevDeps.length !== nextDeps.length) {
108
- return false;
109
- }
110
-
111
- for (let i = 0; i < prevDeps.length; ++i) {
112
- const nextDep = nextDeps[i];
113
- const prevDep = prevDeps[i];
114
- if (objectIs(nextDep, prevDep)) {
115
- continue;
116
- }
117
- if (!isWorkletFunction(nextDep) || !isWorkletFunction(prevDep)) {
118
- return false;
119
- }
120
- if (!areWorkletsEqual(nextDep, prevDep)) {
121
- return false;
122
- }
123
- }
124
- return true;
125
- }
126
-
127
- return areHookInputsEqual(nextDependencies, prevDependencies);
128
- }
2
+ import { ReanimatedError } from '../common';
129
3
 
130
4
  export function isAnimated(prop: unknown) {
131
5
  'worklet';
@@ -19,6 +19,7 @@ export function initializeReanimatedModule(
19
19
  }
20
20
  }
21
21
 
22
+ // is-tree-shakable-suppress
22
23
  if (!SHOULD_BE_USE_WEB) {
23
24
  runOnUISync(() => {
24
25
  'worklet';
@@ -22,3 +22,13 @@ export const worklet = <Args extends unknown[] = [], ReturnValue = void>(
22
22
  fn.__closure = {};
23
23
  return fn;
24
24
  };
25
+
26
+ /** Creates a new worklet with the same hash and closure as the original. */
27
+ export const cloneWorklet = <Args extends unknown[] = [], ReturnValue = void>(
28
+ original: WorkletFunction<Args, ReturnValue>
29
+ ): WorkletFunction<Args, ReturnValue> => {
30
+ const w = worklet<Args, ReturnValue>();
31
+ w.__workletHash = original.__workletHash;
32
+ w.__closure = { ...original.__closure };
33
+ return w;
34
+ };
@@ -2,7 +2,6 @@
2
2
  'use strict';
3
3
 
4
4
  import type React from 'react';
5
- import type { ReactTestInstance } from 'react-test-renderer';
6
5
 
7
6
  import { IS_JEST, logger, ReanimatedError } from '../common';
8
7
  import type {
@@ -347,12 +346,10 @@ type TestComponent = React.Component<
347
346
  }
348
347
  >;
349
348
 
350
- export const getAnimatedStyle = (component: ReactTestInstance) => {
351
- return getCurrentStyle(
352
- // This type assertion is needed to get type checking in the following
353
- // functions since `ReactTestInstance` has its `props` defined as `any`.
354
- component as unknown as TestComponent
355
- );
349
+ export const getAnimatedStyle = (component: {
350
+ props: Record<string, unknown>;
351
+ }) => {
352
+ return getCurrentStyle(component as unknown as TestComponent);
356
353
  };
357
354
 
358
- export { worklet } from './common';
355
+ export { cloneWorklet, worklet } from './common';
@@ -23,4 +23,4 @@ export function getAnimatedStyle() {
23
23
  // NOOP
24
24
  }
25
25
 
26
- export { worklet } from './common';
26
+ export { cloneWorklet, worklet } from './common';
@@ -4,4 +4,4 @@
4
4
  * version used to build the native part of the library in runtime. Remember to
5
5
  * keep this in sync with the version declared in `package.json`
6
6
  */
7
- export const jsVersion = '4.3.0-rc.0';
7
+ export const jsVersion = '4.3.0';
@@ -24,6 +24,10 @@ declare global {
24
24
  var _REANIMATED_VERSION_JS: string | undefined;
25
25
  var __reanimatedModuleProxy: ReanimatedModuleProxy | undefined;
26
26
  var _log: (value: unknown) => void;
27
+ var _startProfiling: (meanHzFreq?: number) => void;
28
+ var _stopProfiling: () => string;
29
+ var _beginSection: (name: string) => void;
30
+ var _endSection: () => void;
27
31
  var _notifyAboutProgress: (
28
32
  tag: number,
29
33
  value: Record<string, unknown>