Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make animated component's event tag properly update #6030

Merged
merged 10 commits into from
Jun 11, 2024

Conversation

szydlovsky
Copy link
Contributor

Summary

Turns out (thanks to @j-piasecki for noticing) that animated component's event-emitting viewTag can change through re-rendering. The following fix takes care of that.

Test plan

You can go through all ComposedEventHandler examples form Example app, as well as code snippets in #5845 (comment).

Also, this should work perfectly regardless of re-renders:

Code
import React, { useState } from 'react';
import { Button, StyleSheet, View } from 'react-native';
import { GestureDetector, Gesture } from 'react-native-gesture-handler';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
} from 'react-native-reanimated';

function Ball(props) {
  const isPressed = useSharedValue(false);
  const offset = useSharedValue({ x: 0, y: 0 });

  const animatedStyles = useAnimatedStyle(() => {
    return {
      transform: [
        { translateX: offset.value.x },
        { translateY: offset.value.y },
        { scale: withSpring(isPressed.value ? 1.2 : 1) },
      ],
      backgroundColor: isPressed.value ? 'yellow' : 'blue',
    };
  });

  const gesture = Gesture.Pan()
    .onBegin(() => {
      'worklet';
      isPressed.value = true;
    })
    .onChange((e) => {
      'worklet';
      offset.value = {
        x: e.changeX + offset.value.x,
        y: e.changeY + offset.value.y,
      };
    })
    .onFinalize(() => {
      'worklet';
      isPressed.value = false;
    });

  return (
    <GestureDetector gesture={gesture}>
      <Animated.View style={[styles.ball, animatedStyles]} key={props.counter} />
    </GestureDetector>
  );
}

export default function Example() {
  const [counter, setCounter] = useState(0);

  return (
    <View style={styles.container}>
      <Button title="Remount" onPress={() => setCounter((prev) => prev + 1)} />
      <Ball counter={counter} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  ball: {
    width: 100,
    height: 100,
    borderRadius: 100,
    backgroundColor: 'blue',
    alignSelf: 'center',
  },
});

@szydlovsky szydlovsky requested a review from tjzel May 17, 2024 15:58
@szydlovsky szydlovsky requested a review from piaskowyk May 21, 2024 20:11
Copy link
Collaborator

@tjzel tjzel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work, you got the idea, but now we need some polishing to ensure proper maintainability in the future.

src/createAnimatedComponent/NativeEventsManager.ts Outdated Show resolved Hide resolved
src/createAnimatedComponent/NativeEventsManager.ts Outdated Show resolved Hide resolved
src/createAnimatedComponent/createAnimatedComponent.tsx Outdated Show resolved Hide resolved
src/createAnimatedComponent/NativeEventsManager.ts Outdated Show resolved Hide resolved
src/createAnimatedComponent/NativeEventsManager.ts Outdated Show resolved Hide resolved
src/createAnimatedComponent/NativeEventsManager.ts Outdated Show resolved Hide resolved
@szydlovsky szydlovsky requested a review from tjzel May 28, 2024 14:42
Copy link
Collaborator

@tjzel tjzel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're almost there!

src/createAnimatedComponent/commonTypes.ts Outdated Show resolved Hide resolved
src/createAnimatedComponent/createAnimatedComponent.tsx Outdated Show resolved Hide resolved
src/createAnimatedComponent/NativeEventsManager.ts Outdated Show resolved Hide resolved
src/createAnimatedComponent/NativeEventsManager.ts Outdated Show resolved Hide resolved
src/createAnimatedComponent/NativeEventsManager.ts Outdated Show resolved Hide resolved
@szydlovsky szydlovsky requested review from tjzel and piaskowyk May 29, 2024 13:38
Copy link
Collaborator

@tjzel tjzel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😎

Copy link
Collaborator

@tjzel tjzel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just check one more thing for me and we gucci. Just make sure to test it thoroughly!

Copy link
Member

@piaskowyk piaskowyk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well done 🫡

@szydlovsky szydlovsky added this pull request to the merge queue Jun 11, 2024
Merged via the queue into main with commit afbfc9a Jun 11, 2024
6 checks passed
@szydlovsky szydlovsky deleted the @szydlovsky/updating-eventTags-fix branch June 11, 2024 13:38
@piaskowyk piaskowyk mentioned this pull request Nov 22, 2024
3 tasks
github-merge-queue bot pushed a commit that referenced this pull request Dec 4, 2024
## Summary

This PR addresses the issue reported at
[https://github.com/software-mansion/react-native-reanimated/issues/6719](https://github.com/software-mansion/react-native-reanimated/issues/6719)
and aims to:

- **Unify the method used to obtain the view tag**, as there are
currently several approaches.
- **Avoid passing a class component to the findNodeHandler**. Instead,
we'll pass a ref to the component, similar to what Expo implemented
here:
[https://github.com/expo/expo/pull/33016](https://github.com/expo/expo/pull/33016).
- **Limit unnecessary invocations of findNodeHandler** to no more than
one call per render.
- **Remove the invocation of findHostInstance from Paper renderer** on
the New Architecture.

**Additional Remarks:**
- When a class component is passed to `createAnimatedComponent`, it will
still fall back to the slow path where we can get a native ref.
- In `NativeEventManager`, we need to call findNodeHandler again after
every render to ensure that the children of `<GestureDetector />`
haven't changed their native tags.
- `LayoutAnimationConfig` still uses findNodeHandler. It requires a
complete refactor of their functionality to eliminate its use, and I
plan to handle this in another PR.
- `findHostInstance_DEPRECATED` always follows the slow path even for
native refs, which is why I've implemented our own version of
`findHostInstance` to optimize the happy path.

Fixes
#6719

Related PRs:
- #6030
- #5960
- #4445
- expo/expo#33016

## Test plan

- [x] check Paper
- [x] check Fabric
- [x] check Web
tjzel pushed a commit that referenced this pull request Dec 13, 2024
## Summary

This PR addresses the issue reported at
[https://github.com/software-mansion/react-native-reanimated/issues/6719](https://github.com/software-mansion/react-native-reanimated/issues/6719)
and aims to:

- **Unify the method used to obtain the view tag**, as there are
currently several approaches.
- **Avoid passing a class component to the findNodeHandler**. Instead,
we'll pass a ref to the component, similar to what Expo implemented
here:
[https://github.com/expo/expo/pull/33016](https://github.com/expo/expo/pull/33016).
- **Limit unnecessary invocations of findNodeHandler** to no more than
one call per render.
- **Remove the invocation of findHostInstance from Paper renderer** on
the New Architecture.

**Additional Remarks:**
- When a class component is passed to `createAnimatedComponent`, it will
still fall back to the slow path where we can get a native ref.
- In `NativeEventManager`, we need to call findNodeHandler again after
every render to ensure that the children of `<GestureDetector />`
haven't changed their native tags.
- `LayoutAnimationConfig` still uses findNodeHandler. It requires a
complete refactor of their functionality to eliminate its use, and I
plan to handle this in another PR.
- `findHostInstance_DEPRECATED` always follows the slow path even for
native refs, which is why I've implemented our own version of
`findHostInstance` to optimize the happy path.

Fixes
#6719

Related PRs:
- #6030
- #5960
- #4445
- expo/expo#33016

## Test plan

- [x] check Paper
- [x] check Fabric
- [x] check Web
tjzel pushed a commit that referenced this pull request Dec 13, 2024
## Summary

This PR addresses the issue reported at
[https://github.com/software-mansion/react-native-reanimated/issues/6719](https://github.com/software-mansion/react-native-reanimated/issues/6719)
and aims to:

- **Unify the method used to obtain the view tag**, as there are
currently several approaches.
- **Avoid passing a class component to the findNodeHandler**. Instead,
we'll pass a ref to the component, similar to what Expo implemented
here:
[https://github.com/expo/expo/pull/33016](https://github.com/expo/expo/pull/33016).
- **Limit unnecessary invocations of findNodeHandler** to no more than
one call per render.
- **Remove the invocation of findHostInstance from Paper renderer** on
the New Architecture.

**Additional Remarks:**
- When a class component is passed to `createAnimatedComponent`, it will
still fall back to the slow path where we can get a native ref.
- In `NativeEventManager`, we need to call findNodeHandler again after
every render to ensure that the children of `<GestureDetector />`
haven't changed their native tags.
- `LayoutAnimationConfig` still uses findNodeHandler. It requires a
complete refactor of their functionality to eliminate its use, and I
plan to handle this in another PR.
- `findHostInstance_DEPRECATED` always follows the slow path even for
native refs, which is why I've implemented our own version of
`findHostInstance` to optimize the happy path.

Fixes
#6719

Related PRs:
- #6030
- #5960
- #4445
- expo/expo#33016

## Test plan

- [x] check Paper
- [x] check Fabric
- [x] check Web
tjzel pushed a commit that referenced this pull request Dec 13, 2024
## Summary

This PR addresses the issue reported at
[https://github.com/software-mansion/react-native-reanimated/issues/6719](https://github.com/software-mansion/react-native-reanimated/issues/6719)
and aims to:

- **Unify the method used to obtain the view tag**, as there are
currently several approaches.
- **Avoid passing a class component to the findNodeHandler**. Instead,
we'll pass a ref to the component, similar to what Expo implemented
here:
[https://github.com/expo/expo/pull/33016](https://github.com/expo/expo/pull/33016).
- **Limit unnecessary invocations of findNodeHandler** to no more than
one call per render.
- **Remove the invocation of findHostInstance from Paper renderer** on
the New Architecture.

**Additional Remarks:**
- When a class component is passed to `createAnimatedComponent`, it will
still fall back to the slow path where we can get a native ref.
- In `NativeEventManager`, we need to call findNodeHandler again after
every render to ensure that the children of `<GestureDetector />`
haven't changed their native tags.
- `LayoutAnimationConfig` still uses findNodeHandler. It requires a
complete refactor of their functionality to eliminate its use, and I
plan to handle this in another PR.
- `findHostInstance_DEPRECATED` always follows the slow path even for
native refs, which is why I've implemented our own version of
`findHostInstance` to optimize the happy path.

Fixes
#6719

Related PRs:
- #6030
- #5960
- #4445
- expo/expo#33016

## Test plan

- [x] check Paper
- [x] check Fabric
- [x] check Web
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants