react-native-reanimated 2.14.4 → 2.15.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 (36) hide show
  1. package/Common/cpp/Registries/EventHandlerRegistry.cpp +8 -2
  2. package/android/.gradle/7.5.1/checksums/checksums.lock +0 -0
  3. package/android/.gradle/7.5.1/checksums/md5-checksums.bin +0 -0
  4. package/android/.gradle/7.5.1/checksums/sha1-checksums.bin +0 -0
  5. package/android/.gradle/7.5.1/dependencies-accessors/dependencies-accessors.lock +0 -0
  6. package/android/.gradle/7.5.1/executionHistory/executionHistory.bin +0 -0
  7. package/android/.gradle/7.5.1/executionHistory/executionHistory.lock +0 -0
  8. package/android/.gradle/7.5.1/fileHashes/fileHashes.bin +0 -0
  9. package/android/.gradle/7.5.1/fileHashes/fileHashes.lock +0 -0
  10. package/android/.gradle/7.5.1/fileHashes/resourceHashesCache.bin +0 -0
  11. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  12. package/android/.gradle/buildOutputCleanup/cache.properties +1 -1
  13. package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
  14. package/android/.gradle/file-system.probe +0 -0
  15. package/android/build.gradle +4 -4
  16. package/android/react-native-reanimated-67-hermes.aar +0 -0
  17. package/android/react-native-reanimated-67-jsc.aar +0 -0
  18. package/android/react-native-reanimated-68-hermes.aar +0 -0
  19. package/android/react-native-reanimated-68-jsc.aar +0 -0
  20. package/android/react-native-reanimated-69-hermes.aar +0 -0
  21. package/android/react-native-reanimated-69-jsc.aar +0 -0
  22. package/android/react-native-reanimated-70-hermes.aar +0 -0
  23. package/android/react-native-reanimated-70-jsc.aar +0 -0
  24. package/android/react-native-reanimated-71-hermes.aar +0 -0
  25. package/android/react-native-reanimated-71-jsc.aar +0 -0
  26. package/android/src/main/java/com/swmansion/reanimated/NodesManager.java +6 -7
  27. package/ios/REANodesManager.m +23 -4
  28. package/lib/reanimated2/hook/useAnimatedReaction.js +12 -5
  29. package/lib/reanimated2/hook/useAnimatedStyle.js +12 -3
  30. package/lib/reanimated2/hook/useDerivedValue.js +8 -1
  31. package/package.json +6 -3
  32. package/src/reanimated2/hook/useAnimatedReaction.ts +9 -5
  33. package/src/reanimated2/hook/useAnimatedStyle.ts +14 -3
  34. package/src/reanimated2/hook/useDerivedValue.ts +8 -1
  35. package/lib/reanimated1/core/Core.test.js +0 -30
  36. package/src/reanimated1/core/Core.test.js +0 -30
@@ -49,8 +49,14 @@ void EventHandlerRegistry::processEvent(
49
49
  return;
50
50
  }
51
51
 
52
- auto eventObject = jsi::Value::createFromJsonUtf8(
53
- rt, reinterpret_cast<uint8_t *>(&eventJSON[0]), eventJSON.size());
52
+ jsi::Value eventObject;
53
+ try {
54
+ eventObject = jsi::Value::createFromJsonUtf8(
55
+ rt, reinterpret_cast<uint8_t *>(&eventJSON[0]), eventJSON.size());
56
+ } catch (std::exception &) {
57
+ // Ignore events with malformed JSON payload.
58
+ return;
59
+ }
54
60
 
55
61
  eventObject.asObject(rt).setProperty(
56
62
  rt, "eventName", jsi::String::createFromUtf8(rt, eventName));
@@ -1,2 +1,2 @@
1
- #Thu Jan 19 15:25:25 UTC 2023
1
+ #Mon Apr 03 12:23:34 UTC 2023
2
2
  gradle.version=7.5.1
Binary file
@@ -289,10 +289,10 @@ def detectAAR(Integer rnMinorVersion, String engine) { // Reanimated2 only
289
289
  println "\n\n\n"
290
290
  println "****************************************************************************************"
291
291
  println "\n\n\n"
292
- println "WARNING reanimated - no version-specific reanimated AAR for react-native version $minor found."
292
+ println "WARNING reanimated - no version-specific reanimated AAR for react-native version $rnMinorVersion found."
293
293
  println "Falling back to AAR for react-native version $rnMinorVersionCopy."
294
294
  println "The react-native JSI interface is not ABI-safe yet, this may result in crashes."
295
- println "Please post a pull request to implement support for react-native version $minor to the reanimated repo."
295
+ println "Please post a pull request to implement support for react-native version $rnMinorVersion to the reanimated repo."
296
296
  println "Thanks!"
297
297
  println "\n\n\n"
298
298
  println "****************************************************************************************"
@@ -1033,13 +1033,13 @@ dependencies {
1033
1033
 
1034
1034
  if (REACT_NATIVE_MINOR_VERSION >= 71) {
1035
1035
  if (isPackageBuild()) {
1036
- implementation "com.facebook.react:react-android:+"
1036
+ implementation "com.facebook.react:react-android:" + REACT_NATIVE_VERSION
1037
1037
  } else {
1038
1038
  implementation "com.facebook.react:react-android:" // version substituted by RNGP
1039
1039
  }
1040
1040
  if (JS_RUNTIME == "hermes") {
1041
1041
  if (isPackageBuild()) {
1042
- implementation "com.facebook.react:hermes-android:+"
1042
+ implementation "com.facebook.react:hermes-android:" + REACT_NATIVE_VERSION
1043
1043
  } else {
1044
1044
  implementation "com.facebook.react:hermes-android:" // version substituted by RNGP
1045
1045
  }
@@ -61,6 +61,7 @@ import java.util.Queue;
61
61
  import java.util.Set;
62
62
  import java.util.concurrent.ConcurrentLinkedQueue;
63
63
  import java.util.concurrent.Semaphore;
64
+ import java.util.concurrent.TimeUnit;
64
65
  import java.util.concurrent.atomic.AtomicBoolean;
65
66
  import javax.annotation.Nullable;
66
67
 
@@ -248,13 +249,11 @@ public class NodesManager implements EventDispatcherListener {
248
249
  }
249
250
  });
250
251
  if (trySynchronously) {
251
- while (true) {
252
- try {
253
- semaphore.acquire();
254
- break;
255
- } catch (InterruptedException e) {
256
- // noop
257
- }
252
+ try {
253
+ semaphore.tryAcquire(16, TimeUnit.MILLISECONDS);
254
+ } catch (InterruptedException e) {
255
+ // if the thread is interruped we just continue and let the layout update happen
256
+ // asynchronously
258
257
  }
259
258
  }
260
259
  }
@@ -109,6 +109,8 @@
109
109
  BOOL _tryRunBatchUpdatesSynchronously;
110
110
  REAEventHandler _eventHandler;
111
111
  volatile void (^_mounting)(void);
112
+ NSObject *_syncLayoutUpdatesWaitLock;
113
+ volatile BOOL _syncLayoutUpdatesWaitTimedOut;
112
114
  NSMutableDictionary<NSNumber *, ComponentUpdate *> *_componentUpdateBuffer;
113
115
  volatile atomic_bool _shouldFlushUpdateBuffer;
114
116
  NSMutableDictionary<NSNumber *, UIView *> *_viewRegistry;
@@ -128,6 +130,7 @@
128
130
  _operationsInBatch = [NSMutableArray new];
129
131
  _componentUpdateBuffer = [NSMutableDictionary new];
130
132
  _viewRegistry = [_uiManager valueForKey:@"_viewRegistry"];
133
+ _syncLayoutUpdatesWaitLock = [NSObject new];
131
134
  _shouldFlushUpdateBuffer = false;
132
135
  }
133
136
 
@@ -232,8 +235,14 @@
232
235
  - (BOOL)uiManager:(RCTUIManager *)manager performMountingWithBlock:(RCTUIManagerMountingBlock)block
233
236
  {
234
237
  RCTAssert(_mounting == nil, @"Mouting block is expected to not be set");
235
- _mounting = block;
236
- return YES;
238
+ @synchronized(_syncLayoutUpdatesWaitLock) {
239
+ if (_syncLayoutUpdatesWaitTimedOut) {
240
+ return NO;
241
+ } else {
242
+ _mounting = block;
243
+ return YES;
244
+ }
245
+ }
237
246
  }
238
247
 
239
248
  - (void)performOperations
@@ -250,6 +259,7 @@
250
259
 
251
260
  __weak typeof(self) weakSelf = self;
252
261
  dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
262
+ _syncLayoutUpdatesWaitTimedOut = NO;
253
263
  RCTExecuteOnUIManagerQueue(^{
254
264
  __typeof__(self) strongSelf = weakSelf;
255
265
  if (strongSelf == nil) {
@@ -266,7 +276,7 @@
266
276
  }
267
277
 
268
278
  if (canUpdateSynchronously) {
269
- [strongSelf.uiManager runSyncUIUpdatesWithObserver:self];
279
+ [strongSelf.uiManager runSyncUIUpdatesWithObserver:strongSelf];
270
280
  dispatch_semaphore_signal(semaphore);
271
281
  }
272
282
  // In case canUpdateSynchronously=true we still have to send uiManagerWillPerformMounting event
@@ -274,7 +284,16 @@
274
284
  [strongSelf.uiManager setNeedsLayout];
275
285
  });
276
286
  if (trySynchronously) {
277
- dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
287
+ // The 16ms timeout here aims to match the frame duration. It may make sense to read that parameter
288
+ // from CADisplayLink but it is easier to hardcode it for the time being.
289
+ // The reason why we use frame duration here is that if takes longer than one frame to complete layout tasks
290
+ // there is no point of synchronizing layout with the UI interaction as we get that one frame delay anyways.
291
+ long result = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 16 * NSEC_PER_MSEC));
292
+ if (result != 0) {
293
+ @synchronized(_syncLayoutUpdatesWaitLock) {
294
+ _syncLayoutUpdatesWaitTimedOut = YES;
295
+ }
296
+ }
278
297
  }
279
298
 
280
299
  if (_mounting) {
@@ -1,6 +1,7 @@
1
1
  import { useEffect } from 'react';
2
2
  import { startMapper, stopMapper } from '../core';
3
3
  import { useSharedValue } from './useSharedValue';
4
+ import { shouldBeUseWeb } from '../PlatformChecker';
4
5
  /**
5
6
  * @param prepare - worklet used for data preparation for the second parameter
6
7
  * @param react - worklet which takes data prepared by the one in the first parameter and performs certain actions
@@ -8,12 +9,19 @@ import { useSharedValue } from './useSharedValue';
8
9
  * the second one can modify any shared values but those which are mentioned in the first worklet. Beware of that, because this may result in endless loop and high cpu usage.
9
10
  */
10
11
  export function useAnimatedReaction(prepare, react, dependencies) {
11
- var _a, _b;
12
+ var _a, _b, _c;
12
13
  const previous = useSharedValue(null);
14
+ let inputs = Object.values((_a = prepare._closure) !== null && _a !== void 0 ? _a : {});
15
+ if (shouldBeUseWeb()) {
16
+ if (!inputs.length && (dependencies === null || dependencies === void 0 ? void 0 : dependencies.length)) {
17
+ // let web work without a Babel/SWC plugin
18
+ inputs = dependencies;
19
+ }
20
+ }
13
21
  if (dependencies === undefined) {
14
22
  dependencies = [
15
- Object.values((_a = prepare._closure) !== null && _a !== void 0 ? _a : {}),
16
- Object.values((_b = react._closure) !== null && _b !== void 0 ? _b : {}),
23
+ Object.values((_b = prepare._closure) !== null && _b !== void 0 ? _b : {}),
24
+ Object.values((_c = react._closure) !== null && _c !== void 0 ? _c : {}),
17
25
  prepare.__workletHash,
18
26
  react.__workletHash,
19
27
  ];
@@ -22,14 +30,13 @@ export function useAnimatedReaction(prepare, react, dependencies) {
22
30
  dependencies.push(prepare.__workletHash, react.__workletHash);
23
31
  }
24
32
  useEffect(() => {
25
- var _a;
26
33
  const fun = () => {
27
34
  'worklet';
28
35
  const input = prepare();
29
36
  react(input, previous.value);
30
37
  previous.value = input;
31
38
  };
32
- const mapperId = startMapper(fun, Object.values((_a = prepare._closure) !== null && _a !== void 0 ? _a : {}), []);
39
+ const mapperId = startMapper(fun, inputs, []);
33
40
  return () => {
34
41
  stopMapper(mapperId);
35
42
  };
@@ -165,7 +165,7 @@ function styleUpdater(viewDescriptors, updater, state, maybeViewRef, animationsA
165
165
  state.isAnimationCancelled = true;
166
166
  state.animations = [];
167
167
  const diff = styleDiff(oldValues, newValues);
168
- state.last = Object.assign({}, oldValues, diff);
168
+ state.last = Object.assign({}, oldValues, newValues);
169
169
  if (diff) {
170
170
  updateProps(viewDescriptors, newValues, maybeViewRef);
171
171
  }
@@ -240,7 +240,7 @@ function jestStyleUpdater(viewDescriptors, updater, state, maybeViewRef, animati
240
240
  }
241
241
  // calculate diff
242
242
  const diff = styleDiff(oldValues, newValues);
243
- state.last = Object.assign({}, oldValues, diff);
243
+ state.last = Object.assign({}, oldValues, newValues);
244
244
  if (Object.keys(diff).length !== 0) {
245
245
  updatePropsJestWrapper(viewDescriptors, diff, maybeViewRef, animatedStyle, adapters);
246
246
  }
@@ -271,7 +271,16 @@ export function useAnimatedStyle(updater, dependencies, adapters) {
271
271
  const viewsRef = makeViewsRefSet();
272
272
  const viewDescriptors = makeViewDescriptorsSet();
273
273
  const initRef = useRef();
274
- const inputs = Object.values((_a = updater._closure) !== null && _a !== void 0 ? _a : {});
274
+ let inputs = Object.values((_a = updater._closure) !== null && _a !== void 0 ? _a : {});
275
+ if (shouldBeUseWeb()) {
276
+ if (!inputs.length && (dependencies === null || dependencies === void 0 ? void 0 : dependencies.length)) {
277
+ // let web work without a Babel/SWC plugin
278
+ inputs = dependencies;
279
+ }
280
+ if (__DEV__ && !inputs.length && !dependencies && !updater.__workletHash) {
281
+ console.error(`useAnimatedStyle was used without a dependency array or Babel plugin. Please explicitly pass a dependency array, or enable the Babel/SWC plugin. For more, see the docs: https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/web-support#web-without-a-babel-plugin`);
282
+ }
283
+ }
275
284
  const adaptersArray = adapters
276
285
  ? Array.isArray(adapters)
277
286
  ? adapters
@@ -1,10 +1,17 @@
1
1
  import { useEffect, useRef } from 'react';
2
2
  import { initialUpdaterRun } from '../animation';
3
3
  import { makeMutable, startMapper, stopMapper } from '../core';
4
+ import { shouldBeUseWeb } from '../PlatformChecker';
4
5
  export function useDerivedValue(processor, dependencies) {
5
6
  var _a;
6
7
  const initRef = useRef(null);
7
- const inputs = Object.values((_a = processor._closure) !== null && _a !== void 0 ? _a : {});
8
+ let inputs = Object.values((_a = processor._closure) !== null && _a !== void 0 ? _a : {});
9
+ if (shouldBeUseWeb()) {
10
+ if (!inputs.length && (dependencies === null || dependencies === void 0 ? void 0 : dependencies.length)) {
11
+ // let web work without a Babel/SWC plugin
12
+ inputs = dependencies;
13
+ }
14
+ }
8
15
  // build dependencies
9
16
  if (dependencies === undefined) {
10
17
  dependencies = [...inputs, processor.__workletHash];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-reanimated",
3
- "version": "2.14.4",
3
+ "version": "2.15.0",
4
4
  "description": "More powerful alternative to Animated library for React Native.",
5
5
  "scripts": {
6
6
  "test": "yarn run format:js && yarn run lint:js && yarn run test:unit",
@@ -77,7 +77,6 @@
77
77
  "dependencies": {
78
78
  "@babel/plugin-transform-object-assign": "^7.16.7",
79
79
  "@babel/preset-typescript": "^7.16.7",
80
- "convert-source-map": "^1.7.0",
81
80
  "invariant": "^2.2.4",
82
81
  "lodash.isequal": "^4.5.0",
83
82
  "setimmediate": "^1.0.5",
@@ -122,6 +121,7 @@
122
121
  "jest": "^27.5.1",
123
122
  "lint-staged": "^11.2.0",
124
123
  "madge": "^5.0.1",
124
+ "metro-react-native-babel-preset": "^0.74.0",
125
125
  "prettier": "^2.5.1",
126
126
  "react": "17.0.2",
127
127
  "react-native": "0.71.0-rc.5",
@@ -161,5 +161,8 @@
161
161
  "./lib/reanimated2/core",
162
162
  "./lib/reanimated2/js-reanimated/global",
163
163
  "./lib/index"
164
- ]
164
+ ],
165
+ "resolutions": {
166
+ "@babel/core": "7.20.12"
167
+ }
165
168
  }
@@ -3,6 +3,7 @@ import { BasicWorkletFunction, WorkletFunction } from '../commonTypes';
3
3
  import { startMapper, stopMapper } from '../core';
4
4
  import { DependencyList } from './commonTypes';
5
5
  import { useSharedValue } from './useSharedValue';
6
+ import { shouldBeUseWeb } from '../PlatformChecker';
6
7
 
7
8
  export interface AnimatedReactionWorkletFunction<T> extends WorkletFunction {
8
9
  (prepared: T, previous: T | null): void;
@@ -19,6 +20,13 @@ export function useAnimatedReaction<T>(
19
20
  dependencies: DependencyList
20
21
  ): void {
21
22
  const previous = useSharedValue<T | null>(null);
23
+ let inputs = Object.values(prepare._closure ?? {});
24
+ if (shouldBeUseWeb()) {
25
+ if (!inputs.length && dependencies?.length) {
26
+ // let web work without a Babel/SWC plugin
27
+ inputs = dependencies;
28
+ }
29
+ }
22
30
  if (dependencies === undefined) {
23
31
  dependencies = [
24
32
  Object.values(prepare._closure ?? {}),
@@ -37,11 +45,7 @@ export function useAnimatedReaction<T>(
37
45
  react(input, previous.value);
38
46
  previous.value = input;
39
47
  };
40
- const mapperId = startMapper(
41
- fun,
42
- Object.values(prepare._closure ?? {}),
43
- []
44
- );
48
+ const mapperId = startMapper(fun, inputs, []);
45
49
  return () => {
46
50
  stopMapper(mapperId);
47
51
  };
@@ -263,7 +263,7 @@ function styleUpdater(
263
263
  state.animations = [];
264
264
 
265
265
  const diff = styleDiff(oldValues, newValues);
266
- state.last = Object.assign({}, oldValues, diff);
266
+ state.last = Object.assign({}, oldValues, newValues);
267
267
  if (diff) {
268
268
  updateProps(viewDescriptors, newValues, maybeViewRef);
269
269
  }
@@ -361,7 +361,7 @@ function jestStyleUpdater(
361
361
 
362
362
  // calculate diff
363
363
  const diff = styleDiff(oldValues, newValues);
364
- state.last = Object.assign({}, oldValues, diff);
364
+ state.last = Object.assign({}, oldValues, newValues);
365
365
 
366
366
  if (Object.keys(diff).length !== 0) {
367
367
  updatePropsJestWrapper(
@@ -409,7 +409,18 @@ export function useAnimatedStyle<T extends AnimatedStyle>(
409
409
  const viewsRef: ViewRefSet<any> = makeViewsRefSet();
410
410
  const viewDescriptors: ViewDescriptorsSet = makeViewDescriptorsSet();
411
411
  const initRef = useRef<AnimationRef>();
412
- const inputs = Object.values(updater._closure ?? {});
412
+ let inputs = Object.values(updater._closure ?? {});
413
+ if (shouldBeUseWeb()) {
414
+ if (!inputs.length && dependencies?.length) {
415
+ // let web work without a Babel/SWC plugin
416
+ inputs = dependencies;
417
+ }
418
+ if (__DEV__ && !inputs.length && !dependencies && !updater.__workletHash) {
419
+ console.error(
420
+ `useAnimatedStyle was used without a dependency array or Babel plugin. Please explicitly pass a dependency array, or enable the Babel/SWC plugin. For more, see the docs: https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/web-support#web-without-a-babel-plugin`
421
+ );
422
+ }
423
+ }
413
424
  const adaptersArray: AdapterWorkletFunction[] = adapters
414
425
  ? Array.isArray(adapters)
415
426
  ? adapters
@@ -3,6 +3,7 @@ import { initialUpdaterRun } from '../animation';
3
3
  import { BasicWorkletFunction, SharedValue } from '../commonTypes';
4
4
  import { makeMutable, startMapper, stopMapper } from '../core';
5
5
  import { DependencyList } from './commonTypes';
6
+ import { shouldBeUseWeb } from '../PlatformChecker';
6
7
 
7
8
  export type DerivedValue<T> = Readonly<SharedValue<T>>;
8
9
 
@@ -11,7 +12,13 @@ export function useDerivedValue<T>(
11
12
  dependencies: DependencyList
12
13
  ): DerivedValue<T> {
13
14
  const initRef = useRef<SharedValue<T> | null>(null);
14
- const inputs = Object.values(processor._closure ?? {});
15
+ let inputs = Object.values(processor._closure ?? {});
16
+ if (shouldBeUseWeb()) {
17
+ if (!inputs.length && dependencies?.length) {
18
+ // let web work without a Babel/SWC plugin
19
+ inputs = dependencies;
20
+ }
21
+ }
15
22
 
16
23
  // build dependencies
17
24
  if (dependencies === undefined) {
@@ -1,30 +0,0 @@
1
- import React from 'react';
2
- import Animated from '../../Animated';
3
-
4
- import renderer from 'react-test-renderer';
5
-
6
- jest.mock('../../ReanimatedEventEmitter');
7
- jest.mock('../../ReanimatedModule');
8
- jest.mock('../../reanimated2/NativeReanimated/NativeReanimated');
9
-
10
- describe('Core Animated components', () => {
11
- xit('fails if something other then a node or function that returns a node is passed to Animated.Code exec prop', () => {
12
- console.error = jest.fn();
13
-
14
- expect(() =>
15
- renderer.create(<Animated.Code exec="not a node" />)
16
- ).toThrowError(
17
- "<Animated.Code /> expects the 'exec' prop or children to be an animated node or a function returning an animated node."
18
- );
19
- });
20
-
21
- xit('fails if something other then a node or function that returns a node is passed to Animated.Code children', () => {
22
- console.error = jest.fn();
23
-
24
- expect(() =>
25
- renderer.create(<Animated.Code>not a node</Animated.Code>)
26
- ).toThrowError(
27
- "<Animated.Code /> expects the 'exec' prop or children to be an animated node or a function returning an animated node."
28
- );
29
- });
30
- });
@@ -1,30 +0,0 @@
1
- import React from 'react';
2
- import Animated from '../../Animated';
3
-
4
- import renderer from 'react-test-renderer';
5
-
6
- jest.mock('../../ReanimatedEventEmitter');
7
- jest.mock('../../ReanimatedModule');
8
- jest.mock('../../reanimated2/NativeReanimated/NativeReanimated');
9
-
10
- describe('Core Animated components', () => {
11
- xit('fails if something other then a node or function that returns a node is passed to Animated.Code exec prop', () => {
12
- console.error = jest.fn();
13
-
14
- expect(() =>
15
- renderer.create(<Animated.Code exec="not a node" />)
16
- ).toThrowError(
17
- "<Animated.Code /> expects the 'exec' prop or children to be an animated node or a function returning an animated node."
18
- );
19
- });
20
-
21
- xit('fails if something other then a node or function that returns a node is passed to Animated.Code children', () => {
22
- console.error = jest.fn();
23
-
24
- expect(() =>
25
- renderer.create(<Animated.Code>not a node</Animated.Code>)
26
- ).toThrowError(
27
- "<Animated.Code /> expects the 'exec' prop or children to be an animated node or a function returning an animated node."
28
- );
29
- });
30
- });