software-mansion / react-native-reanimated

React Native's Animated library reimplemented
https://docs.swmansion.com/react-native-reanimated/
MIT License
9.01k stars 1.3k forks source link

Testing error: useSharedValue is not a function while testing #954

Closed KoboBunny closed 3 years ago

KoboBunny commented 4 years ago

Description

useSharedValue throws an error that it's not a function when using jest and react-native-testing-library. I've mocked react-native-reanimated by using the mock provided as part of the package. I also notice VS Code highlights that reanimated has no exported member useSharedValue, useAnimatedStyle and withTiming, however the component still runs properly without any errors.

Does anyone know what needs to be done to mock it properly?

Screenshots

Screen Shot 2020-07-07 at 2 48 22 PM image

Steps To Reproduce

  1. Create the simple switch component
  2. Create the mock file
  3. Create the test
  4. Run the test

Expected behavior

I expected the render test to pass since I'm successfully mocking react-native-reanimated

Actual behavior

The render test fails due to useSharedValue not being a function. "TypeError: (0 , _reactNativeReanimated.useSharedValue) is not a function"

Snack or minimal code example

The Test to see if the component renders

`import React from "react"; import { render } from "react-native-testing-library"; import { Switch } from "../src/native/Switch";

describe("Switch Package", () => { it("renders", () => { render(); }); });`

Mock file which is placed in mocks named react-native-reanimated.js

jest.mock("react-native-reanimated", () => require("react-native-reanimated/mock") );

The Component using Reanimated - Switch.tsx `import { Button } from 'react-native'; import { useSharedValue, useAnimatedStyle, withTiming, Easing, } from 'react-native-reanimated';

function Switch() { const opacity = useSharedValue(0);

const style = useAnimatedStyle(() => { return { opacity: withTiming(opacity.value, { duration: 200, easing: sing.out(Easing.cubic), }), }; });

return (

terrysahaidak commented 4 years ago

Right now there is no typings for Reanimated2 that's why VSCode shows an error.

There is PR in progress to fix this.

jakub-gonet commented 4 years ago

@KoboBunny, could please verify if that error still shows on the latest alpha?

sravanpronteff commented 4 years ago

@KoboBunny, could please verify if that error still shows on the latest alpha?

still an error

KoboBunny commented 4 years ago

Hi @terrysahaidak

Sorry I didn't get a chance to get back to you! We're not using the alpha at the moment as we wanted to wait for the beta to come out and we could afford to wait. If we start using it again, I'll definitely let you know if we still have this error!

sourabhk-cg commented 4 years ago

Hi @software-mansion I am trying to use the plugin for achieving Animation in React-Native project but facing this issue every-time. Tried with both static and dynamic values. Screenshot_1600773173

terrysahaidak commented 4 years ago

Hi @sourabhk-cg could you post more info? The code would be nice.

sourabhk-cg commented 4 years ago

Hi @terrysahaidak , Following is the code hoping for your help...

import {
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from "react-native-reanimated";
import React, { useEffect, useState } from "react";
import { Animated, Keyboard, Text } from "react-native";

const dummyAnimation = (props: any) => {
  const [isKeyboardOpen, setIsKeyboardOpen] = useState(false);

  useEffect(() => {
    const keyboardDidShowListener = Keyboard.addListener(
      "keyboardDidShow",
      () => {
        setIsKeyboardOpen(true);
      }
    );
    const keyboardDidHideListener = Keyboard.addListener(
      "keyboardDidHide",
      () => {
        setIsKeyboardOpen(false);
      }
    );
    return () => {
      keyboardDidHideListener.remove();
      keyboardDidShowListener.remove();
    };
  }, []);
  const borderRadius = useSharedValue(22);
  const borderRadiusShowStyle = useAnimatedStyle(() => {
    return {
      width: withTiming(0, {
        duration: 400,
      }),
    };
  });
  const borderRadiusHideStyle = useAnimatedStyle(() => {
    return {
      width: withTiming(borderRadius.value, {
        duration: 400,
      }),
    };
  });

  return (
    <Animated.View
      style={[ isKeyboardOpen ? borderRadiusShowStyle : borderRadiusHideStyle ]}
    >
      <Text>{"this is animating"}</Text>
    </Animated.View>
  );
};
terrysahaidak commented 4 years ago

if you have just installed Reanimated, make sure to run packager again with --reset-cache.

Also, for your animation you can do something like that:

const dummyAnimation = (props: any) => {
  const [isKeyboardOpen, setIsKeyboardOpen] = useState(false);

  useEffect(() => {
    const keyboardDidShowListener = Keyboard.addListener(
      "keyboardDidShow",
      () => {
        setIsKeyboardOpen(true);
      }
    );
    const keyboardDidHideListener = Keyboard.addListener(
      "keyboardDidHide",
      () => {
        setIsKeyboardOpen(false);
      }
    );
    return () => {
      keyboardDidHideListener.remove();
      keyboardDidShowListener.remove();
    };
  }, []);

  const borderRadiusStyle = useAnimatedStyle(() => {
    return {
      width: withTiming(isKeyboardOpen ? 22 : 0, {
        duration: 400,
      }),
    };
  }, [isKeyboardOpen]);

  return (
    <Animated.View
      style={borderRadiusStyle}
    >
      <Text>{"this is animating"}</Text>
    </Animated.View>
  );
};
sourabhk-cg commented 4 years ago

if you have just installed Reanimated, make sure to run packager again with --reset-cache.

Also, for your animation you can do something like that:

const dummyAnimation = (props: any) => {
  const [isKeyboardOpen, setIsKeyboardOpen] = useState(false);

  useEffect(() => {
    const keyboardDidShowListener = Keyboard.addListener(
      "keyboardDidShow",
      () => {
        setIsKeyboardOpen(true);
      }
    );
    const keyboardDidHideListener = Keyboard.addListener(
      "keyboardDidHide",
      () => {
        setIsKeyboardOpen(false);
      }
    );
    return () => {
      keyboardDidHideListener.remove();
      keyboardDidShowListener.remove();
    };
  }, []);

  const borderRadiusStyle = useAnimatedStyle(() => {
    return {
      width: withTiming(isKeyboardOpen ? 22 : 0, {
        duration: 400,
      }),
    };
  }, [isKeyboardOpen]);

  return (
    <Animated.View
      style={borderRadiusStyle}
    >
      <Text>{"this is animating"}</Text>
    </Animated.View>
  );
};

Hi @terrysahaidak, Thank you for your quick reply, but I am sorry to say that I tried your suggestions but still getting the same error in my code.

terrysahaidak commented 4 years ago

What version of Reanimated did you use?

sourabhk-cg commented 4 years ago

What version of Reanimated did you use?

I am using following version: "react-native-reanimated": "^2.0.0-alpha.6" and following this document URL for installation. Installation Document

terrysahaidak commented 4 years ago

Could you try to reinstall node_modules? I mean remove them completely before installation

sourabhk-cg commented 4 years ago

Could you try to reinstall node_modules? I mean remove them completely before installation

I tried removing and re-installing node_moduels and then completely removed application from emulator and build folders as well and then tried all installation steps and run the app, but still same result.

terrysahaidak commented 4 years ago

Sorry, then I won't be able to help without the access to code. But still, check out if the following functions exist in the react-native-reanimated in node_modules. That's the best place to start.

sourabhk-cg commented 4 years ago

Sorry, then I won't be able to help without the access to code. But still, check out if the following functions exist in the react-native-reanimated in node_modules. That's the best place to start.

Okay, but the code I shared is the complete which I was trying for the first time on my page, and I checked for the function that is also available in node_modules of react-native-reanimated. Anyway thanks for your help. 👍

jakub-gonet commented 4 years ago

@sourabhk-cg, please show the content of node_modules/react-native-reanimated/src/index.js, node_modules/react-native-reanimated/src/reanimated2/index.js and node_modules/react-native-reanimated/src/reanimated2/Hooks.js. Your babel config may be helpful as well.

You may try clearing the watchman cache too, sometimes it helps.

sourabhk-cg commented 4 years ago

src/reanimated2/index.js

export * from './core';
export * from './Hooks';
export * from './animations';
export * from './interpolation';
export * from './Easing';
export * from './NativeMethods';
export {default as processColor} from "./Colors";

src/reanimated2/Hooks.js

import { useEffect, useRef } from 'react';

import WorkletEventHandler from './WorkletEventHandler';
import { startMapper, stopMapper, makeMutable, makeRemote } from './core';
import updateProps from './UpdateProps';
import { initialUpdaterRun } from './animations';
import { getTag } from './NativeMethods';

export function useSharedValue(init) {
  const ref = useRef(null);
  if (ref.current === null) {
    ref.current = {
      mutable: makeMutable(init),
      last: init,
    };
  } else if (init !== ref.current.last) {
    ref.current.last = init;
    ref.current.mutable.value = init;
  }

  return ref.current.mutable;
}

export function useMapper(fun, inputs = [], outputs = [], dependencies = []) {
  useEffect(() => {
    const mapperId = startMapper(fun, inputs, outputs);
    return () => {
      stopMapper(mapperId);
    };
  }, dependencies);
}

export function useEvent(handler, eventNames = [], rebuild = false) {
  const initRef = useRef(null);
  if (initRef.current === null) {
    initRef.current = new WorkletEventHandler(handler, eventNames);
  } else if (rebuild) {
    initRef.current.worklet = handler;
  }
  return initRef.current;
}

function prepareAnimation(animatedProp, lastAnimation, lastValue) {
  'worklet';
  function prepareAnimation(animatedProp, lastAnimation, lastValue) {
    if (Array.isArray(animatedProp)) {
      animatedProp.forEach((prop, index) =>
        prepareAnimation(
          prop,
          lastAnimation && lastAnimation[index],
          lastValue && lastValue[index]
        )
      );
      return animatedProp;
    }
    if (typeof animatedProp === 'object' && animatedProp.animation) {
      const animation = animatedProp;

      let value = animation.current;
      if (lastValue !== undefined) {
        if (typeof lastValue === 'object') {
          if (lastValue.value !== undefined) {
            // previously it was a shared value
            value = lastValue.value;
          } else if (lastValue.animation !== undefined) {
            // it was an animation before, copy its state
            value = lastAnimation.current;
          }
        } else {
          // previously it was a plan value, just set it as starting point
          value = lastValue;
        }
      }

      animation.callStart = (timestamp) => {
        animation.start(animation, value, timestamp, lastAnimation);
      };
    } else if (typeof animatedProp === 'object') {
      // it is an object
      Object.keys(animatedProp).forEach((key) =>
        prepareAnimation(
          animatedProp[key],
          lastAnimation && lastAnimation[key],
          lastValue && lastValue[key]
        )
      );
    }
  }
  return prepareAnimation(animatedProp, lastAnimation, lastValue);
}

function runAnimations(animation, timestamp, key, result) {
  'worklet';
  function runAnimations(animation, timestamp, key, result) {
    if (Array.isArray(animation)) {
      result[key] = [];
      let allFinished = true;
      animation.forEach((entry, index) => {
        if (!runAnimations(entry, timestamp, index, result[key])) {
          allFinished = false;
        }
      });
      return allFinished;
    } else if (typeof animation === 'object' && animation.animation) {
      if (animation.callStart) {
        animation.callStart(timestamp);
        animation.callStart = null;
      }
      const finished = animation.animation(animation, timestamp);
      animation.timestamp = timestamp;
      if (finished) {
        animation.finished = true;
        animation.callback && animation.callback(true /* finished */);
      }
      result[key] = animation.current;
      return finished;
    } else if (typeof animation === 'object') {
      result[key] = {};
      let allFinished = true;
      Object.keys(animation).forEach((k) => {
        if (!runAnimations(animation[k], timestamp, k, result[key])) {
          allFinished = false;
        }
      });
      return allFinished;
    } else {
      result[key] = animation;
      return true;
    }
  }
  return runAnimations(animation, timestamp, key, result);
}

// TODO: recirsive worklets aren't supported yet
function isAnimated(prop) {
  'worklet';
  function isAnimated(prop) {
    if (Array.isArray(prop)) {
      return prop.some(isAnimated);
    }
    if (typeof prop === 'object') {
      if (prop.animation) {
        return true;
      }
      return Object.keys(prop).some((key) => isAnimated(prop[key]));
    }
    return false;
  }
  return isAnimated(prop);
}

function styleDiff(oldStyle, newStyle) {
  'worklet';
  const diff = {};
  Object.keys(oldStyle).forEach((key) => {
    if (newStyle[key] === undefined) {
      diff[key] = null;
    }
  });
  Object.keys(newStyle).forEach((key) => {
    const value = newStyle[key];
    const oldValue = oldStyle[key];

    if (isAnimated(value)) {
      // do nothing
      return;
    }
    if (
      oldValue !== value &&
      JSON.stringify(oldValue) !== JSON.stringify(value)
    ) {
      // I'd use deep equal here but that'd take additional work and this was easier
      diff[key] = value;
    }
  });
  return diff;
}

function styleUpdater(viewTag, updater, state) {
  'worklet';
  const animations = state.animations || {};

  const newValues = updater() || {};
  const oldValues = state.last;

  // extract animated props
  let hasAnimations = false;
  Object.keys(animations).forEach((key) => {
    const value = newValues[key];
    if (!isAnimated(value)) {
      delete animations[key];
    }
  });
  Object.keys(newValues).forEach((key) => {
    const value = newValues[key];
    if (isAnimated(value)) {
      prepareAnimation(value, animations[key], oldValues[key]);
      animations[key] = value;
      hasAnimations = true;
    }
  });

  function frame(timestamp) {
    const { animations, last, isAnimationCancelled } = state;
    if (isAnimationCancelled) {
      state.isAnimationRunning = false;
      return;
    }

    const updates = {};
    let allFinished = true;
    Object.keys(animations).forEach((propName) => {
      const finished = runAnimations(
        animations[propName],
        timestamp,
        propName,
        updates
      );
      if (finished) {
        last[propName] = updates[propName];
        delete animations[propName];
      } else {
        allFinished = false;
      }
    });

    if (Object.keys(updates).length) {
      updateProps(viewTag.value, updates);
    }

    if (!allFinished) {
      requestAnimationFrame(frame);
    } else {
      state.isAnimationRunning = false;
    }
  }

  if (hasAnimations) {
    state.animations = animations;
    if (!state.isAnimationRunning) {
      state.isAnimationCancelled = false;
      state.isAnimationRunning = true;
      requestAnimationFrame(frame);
    }
  } else {
    state.isAnimationCancelled = true;
    state.animations = {};
  }

  // calculate diff
  const diff = styleDiff(oldValues, newValues);
  state.last = Object.assign({}, oldValues, newValues);

  if (Object.keys(diff).length !== 0) {
    updateProps(viewTag.value, diff);
  }
}

export function useAnimatedStyle(updater, dependencies) {
  const viewTag = useSharedValue(-1);
  const initRef = useRef(null);
  const inputs = Object.values(updater._closure);

  // build dependencies
  if (dependencies === undefined) {
    dependencies = [...inputs, updater.__workletHash];
  } else {
    dependencies.push(updater.__workletHash);
  }

  if (initRef.current === null) {
    const initial = initialUpdaterRun(updater);
    initRef.current = {
      initial,
      remoteState: makeRemote({ last: initial }),
    };
  }

  const { remoteState, initial } = initRef.current;

  useEffect(() => {
    const fun = () => {
      'worklet';
      styleUpdater(viewTag, updater, remoteState);
    };
    const mapperId = startMapper(fun, inputs, []);
    return () => {
      stopMapper(mapperId);
    };
  }, dependencies);

  // check for invalid usage of shared values in returned object
  let wrongKey;
  const isError = Object.keys(initial).some((key) => {
    const element = initial[key];
    const result = typeof element === 'object' && element.value !== undefined;
    if (result) {
      wrongKey = key;
    }
    return result;
  });
  if (isError && wrongKey !== undefined) {
    throw new Error(
      `invalid value passed to \`${wrongKey}\`, maybe you forgot to use \`.value\`?`
    );
  }

  return {
    viewTag,
    initial,
  };
}

// TODO: we should make sure that when useAP is used we are not assigning styles
// when you need styles to animated you should always use useAS
export const useAnimatedProps = useAnimatedStyle;

export function useDerivedValue(processor, dependencies) {
  const initRef = useRef(null);
  const inputs = Object.values(processor._closure);

  // build dependencies
  if (dependencies === undefined) {
    dependencies = [...inputs, processor.__workletHash];
  } else {
    dependencies.push(processor.__workletHash);
  }

  if (initRef.current === null) {
    initRef.current = makeMutable(initialUpdaterRun(processor));
  }

  const sharedValue = initRef.current;

  useEffect(() => {
    const fun = () => {
      'worklet';
      sharedValue.value = processor();
    };
    const mapperId = startMapper(fun, inputs, [sharedValue]);
    return () => {
      stopMapper(mapperId);
    };
  }, dependencies);

  return sharedValue;
}

// builds one big hash from multiple worklets' hashes
function buildWorkletsHash(handlers) {
  return Object.keys(handlers).reduce(
    (previousValue, key) =>
      previousValue === null
        ? handlers[key].__workletHash
        : previousValue.toString() + handlers[key].__workletHash.toString(),
    null
  );
}

// builds dependencies array for gesture handlers
function buildDependencies(dependencies, handlers) {
  if (dependencies === undefined) {
    dependencies = Object.keys(handlers).map((handlerKey) => {
      const handler = handlers[handlerKey];
      return {
        workletHash: handler.__workletHash,
        closure: handler._closure,
      };
    });
  } else {
    dependencies.push(buildWorkletsHash(handlers));
  }
  return dependencies;
}

// this is supposed to work as useEffect comparison
function areDependenciesEqual(nextDeps, prevDeps) {
  function is(x, y) {
    /* eslint-disable no-self-compare */
    return (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y);
    /* eslint-enable no-self-compare */
  }
  var objectIs = typeof Object.is === 'function' ? Object.is : is;

  function areHookInputsEqual(nextDeps, prevDeps) {
    if (prevDeps === null) return !1;
    for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++)
      if (!objectIs(nextDeps[i], prevDeps[i])) return !1;
    return !0;
  }

  return areHookInputsEqual(nextDeps, prevDeps);
}

export function useAnimatedGestureHandler(handlers, dependencies) {
  const initRef = useRef(null);
  if (initRef.current === null) {
    initRef.current = {
      context: makeRemote({}),
      savedDependencies: [],
    };
  }
  const { context, savedDependencies } = initRef.current;

  dependencies = buildDependencies(dependencies, handlers);

  const dependenciesDiffer = !areDependenciesEqual(
    dependencies,
    savedDependencies
  );
  initRef.current.savedDependencies = dependencies;

  return useEvent(
    (event) => {
      'worklet';
      const FAILED = 1;
      const BEGAN = 2;
      const CANCELLED = 3;
      const ACTIVE = 4;
      const END = 5;

      if (event.state === BEGAN && handlers.onStart) {
        handlers.onStart(event, context);
      }
      if (event.state === ACTIVE && handlers.onActive) {
        handlers.onActive(event, context);
      }
      if (event.oldState === ACTIVE && event.state === END && handlers.onEnd) {
        handlers.onEnd(event, context);
      }
      if (
        event.oldState === BEGAN &&
        event.state === FAILED &&
        handlers.onFail
      ) {
        handlers.onFail(event, context);
      }
      if (
        event.oldState === ACTIVE &&
        event.state === CANCELLED &&
        handlers.onCancel
      ) {
        handlers.onCancel(event, context);
      }
      if (
        (event.oldState === BEGAN || event.oldState === ACTIVE) &&
        event.state !== BEGAN &&
        event.state !== ACTIVE &&
        handlers.onFinish
      ) {
        handlers.onFinish(
          event,
          context,
          event.state === CANCELLED || event.state === FAILED
        );
      }
    },
    ['onGestureHandlerStateChange', 'onGestureHandlerEvent'],
    dependenciesDiffer
  );
}

export function useAnimatedScrollHandler(handlers, dependencies) {
  const initRef = useRef(null);
  if (initRef.current === null) {
    initRef.current = {
      context: makeRemote({}),
      savedDependencies: [],
    };
  }
  const { context, savedDependencies } = initRef.current;

  dependencies = buildDependencies(dependencies, handlers);

  const dependenciesDiffer = !areDependenciesEqual(
    dependencies,
    savedDependencies
  );
  initRef.current.savedDependencies = dependencies;

  // build event subscription array
  const subscribeForEvents = ['onScroll'];
  if (handlers.onBeginDrag !== undefined) {
    subscribeForEvents.push('onScrollBeginDrag');
  }
  if (handlers.onEndDrag !== undefined) {
    subscribeForEvents.push('onScrollEndDrag');
  }
  if (handlers.onMomentumBegin !== undefined) {
    subscribeForEvents.push('onMomentumScrollBegin');
  }
  if (handlers.onMomentumEnd !== undefined) {
    subscribeForEvents.push('onMomentumScrollEnd');
  }

  return useEvent(
    (event) => {
      'worklet';
      const {
        onScroll,
        onBeginDrag,
        onEndDrag,
        onMomentumBegin,
        onMomentumEnd,
      } = handlers;
      if (event.eventName.endsWith('onScroll')) {
        if (onScroll) {
          onScroll(event, context);
        } else if (typeof handlers === 'function') {
          handlers(event, context);
        }
      } else if (onBeginDrag && event.eventName.endsWith('onScrollBeginDrag')) {
        onBeginDrag(event, context);
      } else if (onEndDrag && event.eventName.endsWith('onScrollEndDrag')) {
        onEndDrag(event, context);
      } else if (
        onMomentumBegin &&
        event.eventName.endsWith('onMomentumScrollBegin')
      ) {
        onMomentumBegin(event, context);
      } else if (
        onMomentumEnd &&
        event.eventName.endsWith('onMomentumScrollEnd')
      ) {
        onMomentumEnd(event, context);
      }
    },
    subscribeForEvents,
    dependenciesDiffer
  );
}

export function useAnimatedRef() {
  const tag = useSharedValue(-1);
  const ref = useRef(null);

  if (!ref.current) {
    const fun = function(component) {
      'worklet';
      // enters when ref is set by attaching to a component
      if (component) {
        tag.value = getTag(component);
        fun.current = component;
      }
      return tag.value;
    };

    Object.defineProperty(fun, 'current', {
      value: null,
      writable: true,
      enumerable: false,
    });
    ref.current = fun;
  }

  return ref.current;
}

/**
 * @param prepare - worklet used for data preparation for the second parameter
 * @param react - worklet which takes data prepared by the one in the first parameter and performs certain actions
 * the first worklet defines the inputs, in other words on which shared values change will it be called.
 * 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.
 */
export function useAnimatedReaction(prepare, react) {
  const inputsRef = useRef(null);
  if (inputsRef.current === null) {
    inputsRef.current = {
      inputs: Object.values(prepare._closure),
    };
  }
  const { inputs } = inputsRef.current;

  useEffect(() => {
    const fun = () => {
      'worklet';
      const input = prepare();
      react(input);
    };
    const mapperId = startMapper(fun, inputs, []);
    return () => {
      stopMapper(mapperId);
    };
  }, inputs);
}

Our node_modules does not have file named react-native-reanimated/index.js and our project does not have bable.config.js, should I create a new file for bable config? If yes will you be able to help me with the format?

jakub-gonet commented 4 years ago

If you don't have babel config how did you install Reanimated2 and use it's plugin then? I'm afraid I can't help you without reproducing repo.

terrysahaidak commented 4 years ago

Could you try to use import from "src"

import {
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from "react-native-reanimated/src/Animated";

But also it's weird your project doesn't have a babel config file. At least the default one should be

sourabhk-cg commented 4 years ago

Hi @terrysahaidak, sorry to ping you again for same, I just tried the plugin with newly created fresh application, and followed the given document approach. But as soon as I put my app in debug mode it crashes, it was happening with my existing project so I tried with fresh one but same result. Will you be able to give some insight on this? In new project we have the babel.config.js as expected and we defined the properties as well.

sourabhk-cg commented 4 years ago

Could you try to use import from "src"

import {
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from "react-native-reanimated/src/Animated";

But also it's weird your project doesn't have a babel config file. At least the default one should be

I tried this solution as well but this also did not work.

terrysahaidak commented 4 years ago

Hi @terrysahaidak, sorry to ping you again for same, I just tried the plugin with newly created fresh application, and followed the given document approach. But as soon as I put my app in debug mode it crashes, it was happening with my existing project so I tried with fresh one but same result. Will you be able to give some insight on this? In new project we have the babel.config.js as expected and we defined the properties as well.

Unfortunately, debug mode is not supported. The app won't work because of turbo modules.

This is known limitation of Reanimated and TurboModules itself. Please, read carefully the limitations sections of the docs.

sourabhk-cg commented 4 years ago

if you have just installed Reanimated, make sure to run packager again with --reset-cache.

Also, for your animation you can do something like that:

const dummyAnimation = (props: any) => {
  const [isKeyboardOpen, setIsKeyboardOpen] = useState(false);

  useEffect(() => {
    const keyboardDidShowListener = Keyboard.addListener(
      "keyboardDidShow",
      () => {
        setIsKeyboardOpen(true);
      }
    );
    const keyboardDidHideListener = Keyboard.addListener(
      "keyboardDidHide",
      () => {
        setIsKeyboardOpen(false);
      }
    );
    return () => {
      keyboardDidHideListener.remove();
      keyboardDidShowListener.remove();
    };
  }, []);

  const borderRadiusStyle = useAnimatedStyle(() => {
    return {
      width: withTiming(isKeyboardOpen ? 22 : 0, {
        duration: 400,
      }),
    };
  }, [isKeyboardOpen]);

  return (
    <Animated.View
      style={borderRadiusStyle}
    >
      <Text>{"this is animating"}</Text>
    </Animated.View>
  );
};

Hi @terrysahaidak , I referred the document's limitation section and found out I can work with flipper so I did the setup of that and now able to see log, but in above code as you corrected me earlier, I am getting 2 odd things,

  1. useAnimatedStyle hook where I am passing one parameter which is "isKeyboardOpen" I am getting an error in code, expected 1 argument but got 2 and
  2. I tried consoling the value of constant "borderRadiusStyle" that is not changing on change of keyboard open and close from ternary condition.

By any chance will you be able to help please?

hannojg commented 4 years ago

We hotfixed it in our jest.setup file, by doing this:

const mock = jest.requireMock("react-native-reanimated");
jest.mock("react-native-reanimated", () => {
  return {
    ...mock,
    useSharedValue: jest.fn,
    useAnimatedStyle: jest.fn,
  };
});

however, I am not sure if the component which is being tested still behaves correctly with that.

jakub-gonet commented 3 years ago

Closing as duplicate of #1195.

1230 should fix this and other similar errors.

dynamichny commented 3 years ago

We hotfixed it in our jest.setup file, by doing this:

const mock = jest.requireMock("react-native-reanimated");
jest.mock("react-native-reanimated", () => {
  return {
    ...mock,
    useSharedValue: jest.fn,
    useAnimatedStyle: jest.fn,
  };
});

however, I am not sure if the component which is being tested still behaves correctly with that.

requireMock("react-native-reanimated/mock");

worked for me.

shubhamranjan312 commented 5 months ago
TypeError: _react.default.useRef.mockReturnValueOnce is not a function