4

I have a reanimated ReadOnly<SharedValue<boolean>> that is derived from another SharedValue:

  const solidHeader = useDerivedValue(() => {
    return top.value <= -(height / 2);
  });

I would like to call a RN function (non-reanimated) when solidHeader.value changes. Concretely I would like to update my react-navigation's header transparency:

// This doesn't get called when `solidHeader.value` is updated in reanimated's thread
useEffect(() => {
  navigation.setOptions({headerTransparent: !solidHeader.value});
}, [solidHeader.value]);

I have tried the following which "seems" to be the correct way, but then I get the error

Reanimated: Animated.call node args should be an array with elements of type AnimatedNode. One or more of them are not AnimatedNodes
    useCode(() => {
      return call([solidHeader], (solidHeader) => {
        console.warn(solidHeader);
        navigation.setOptions({
          headerTransparent: !solidHeader,
        });
      });
    }, [solidHeader]);

Is what I'm achieving even possible?

Richard
  • 828
  • 1
  • 8
  • 28

2 Answers2

1

EDIT: see the accepted answer.

So this is the hack that I ended up doing: I think the reason why this is so janky is because Reanimated intentionally avoids communicating over the JS/Reanimated threads, so I have to "poll" on the JS side to "depend" on changes on the Reanimated side:

  // top is a SharedValue
  const { top, height } = useHeaderMeasurements();
  const [headerTransparent, setHeaderTransparent] = useState(
    top.value >= -(height / 2)
  );

  useEffect(() => {
    // Poll 60FPS and update RN state
    const check = setInterval(() => {
      setHeaderTransparent(top.value >= -(height / 2));
    }, 1000 / 60);
    return () => clearInterval(check);
  }, [top]);

  useEffect(() => {
    navigation.setOptions({ headerTransparent });
  }, [navigation, headerTransparent]);

For context:

  • useHeaderMeasurements is from react-native-collapsible-tab-view which uses Reanimated V2 under the hood
  • navigation.setOptions is from react-navigation, which requires us to set headerTransparent imperatively on the RN side when we're inside a Screen
Richard
  • 828
  • 1
  • 8
  • 28
0

I think useCode is for reanimated 1 and useSharedValue is for reanimated 2. follow the doc: Try useAnimatedReaction

Alireza Hadjar
  • 450
  • 4
  • 10
  • 1
    I tried doing the following: `useAnimatedReaction(() => top.value >= -(height / 2), (result, previous) => { if (result !== previous) { navigation.setOptions({ headerTransparent: result }); console.log(result); } }, [navigation]);` And it seems to crash, unless I comment out `navigation.setOptions`. It seems like it's not possible to run any JS code inside the second worklet's body? – Richard Sep 06 '21 at 17:49
  • Yeah right. you should wrap your js code inside runOnJs() follow this [link](https://docs.swmansion.com/react-native-reanimated/docs/next/api/runOnJS/) – Alireza Hadjar Sep 07 '21 at 04:39
  • 1
    Perfect, that worked. I'll mark your answer complete (and edit the answer w/ the solution). – Richard Sep 07 '21 at 21:28