Skip to content

Fix crash animated #51442

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

riteshshukla04
Copy link
Contributor

Summary:

Fixes #51395 .

Changelog:

[GENERAL][FIXED]Crash when Animated value & primitive value used together

Test Plan:

Can be tested on RNTester with

const myAnimatedValue = useAnimatedValue(100);
  return (
    <View style={styles.main}>
      <Animated.View style={[
        {width: 100, height: 100, backgroundColor: 'red'},
        {
          transform: [
            {translateX: myAnimatedValue},
          ],
        },
        {
          transform: [
            {translateY: 100},
          ],
        },

       ]} />
    </View>
  );

@facebook-github-bot facebook-github-bot added CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. labels May 18, 2025
@riteshshukla04
Copy link
Contributor Author

riteshshukla04 commented May 18, 2025

When an array of styles are passed its get flattened here

): ?AnimatedStyle {
const flatStyle = flattenStyle(inputStyle);
if (flatStyle == null) {

Now if array with animated object keys is passed to flattened style

[
        {width: 100, height: 100, backgroundColor: 'red'},
        {
          transform: [
            {translateX: myAnimatedValue2},
          ],
        },
        {
          transform: [
            {translateY: 100},
          ],
        },

       ]

The result is below

{
    "width": 100,
    "height": 100,
    "backgroundColor": "red",
    "transform": [
        {
            "translateY": 100
        }
    ]
}

It can be seen that {translateX: myAnimatedValue2} disappears completely .
Now when this flattened object is passed to

const [nodeKeys, nodes, style] = createAnimatedStyle(
flatStyle,
allowlist,
Platform.OS !== 'web',
);
if (nodes.length === 0) {
return null;

We don't recieve any animated nodes cause it doesn't exist in flattened array causing it to return null.

These tricks it into passing an style array(before flattening) with animated values to View component which causes this crash.
My fix here is to flatten style everytime if it is an array in animated component itself

Copy link

@Hardanish-Singh Hardanish-Singh left a comment

Choose a reason for hiding this comment

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

LGTM!

@facebook-github-bot
Copy link
Contributor

@rshest has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

Copy link
Member

@javache javache left a comment

Choose a reason for hiding this comment

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

Thanks for fixing this. Please add a unit test that shows how this behaviour previously broke.

Please also revert the changes to LogBoxInspectorSourceMapStatus

@riteshshukla04
Copy link
Contributor Author

Thanks so much for the review, @javache! 🙏 .
I'm a bit unsure about how to write a reliable Jest test case for this. I tried the following:

const tree =  await render.create(
        <Animated.View
          style={[
            {
              backgroundColor: 'red',
              width: 100,
              height: 100,
            },
            {transform: [{translateX: new Animated.Value(0)}]},
            [{transform: [{translateY: 100}]}],
          ]}
        />
         
      );
      const root = tree.toJSON();

      expect(root).toMatchSnapshot();
      

However, it seems to pass even in scenarios where the component crashes on device.
Should I add an example in RNTester instead?

Also we need to update the snapshot for as the current fix involves flattening style arrays passed to Animated. The snapshot for LogBoxInspectorSourceMapStatus still expects a nested style array, which no longer reflects the updated structure.

@javache
Copy link
Member

javache commented May 19, 2025

Instead of flattening, let's make sure all props passed through have values applied.

You won't see this in jest, since processTransform is only called at the mounting layer. You'd need to validate there's no more Animated.Value in the final props.

@riteshshukla04
Copy link
Contributor Author

Thanks so much for the response, @javache! 🙏

You're absolutely right — though from what I understand, validating all the props might not be very helpful in this case, since they’ll likely be overwritten anyway during the later stages when styles are further flattened (especially in the cases that are currently crashing).

Also, to properly apply all the style values when props are passed, it seems we would need to modify createAnimatedStyle itself to support working with style arrays directly, rather than flattened style object.

const flatStyle = flattenStyle(inputStyle);
if (flatStyle == null) {
return null;
}
const [nodeKeys, nodes, style] = createAnimatedStyle(
flatStyle,
allowlist,
Platform.OS !== 'web',
);

Please do correct me if I’ve misunderstood anything — happy to adjust the approach accordingly!

@riteshshukla04
Copy link
Contributor Author

Instead of flattening, let's make sure all props passed through have values applied.

You won't see this in jest, since processTransform is only called at the mounting layer. You'd need to validate there's no more Animated.Value in the final props.

Hey @javache , I was thinking about this yesterday night and now I think we can do this too .
Something like below will apply all the values and still preserve the array.

if (key === 'style' && Array.isArray(maybeNode)) {
       props[key] = maybeNode.map(style=>{
        const node = AnimatedStyle.from(style, undefined);
        if(node instanceof AnimatedStyle){
          return  node.__getValueWithStaticStyle(style);
        }
        return style;
       })
      }

Should we do something like this ? Whats your opinion?

@javache
Copy link
Member

javache commented May 20, 2025

I think the fix you're looking for needs to be in getValueWithStaticStyle (

__getValueWithStaticStyle(staticStyle: Object): Object | Array<Object> {
)

@javache
Copy link
Member

javache commented May 20, 2025

It's not super clear why we are flattening props there when we can see that in the end a series of unflattened props are passed through to the host component - that's incorrect.

@riteshshukla04
Copy link
Contributor Author

riteshshukla04 commented May 20, 2025

Hey @javache ,
__getValueWithStaticStyle wont be available unless node is an instance of Animated Style (which here is not as the animated values disappear after flattening) .
I can confirm that if we find a animated node then flattened props are passed to host component else non flattened.
The fix I was thinking here is either converting all the props to flattened or go through each element in style array and make the animated values apply before sending it to host component

@javache
Copy link
Member

javache commented May 20, 2025

__getValueWithStaticStyle wont be available unless node is an instance of Animated Style (which here is not as the animated values disappear after flattening) .

Good catch. Perhaps that's a bug that should be fixed here first? If there's an animated value in the unflattened style we should still allocate the Animated.Style.

cc @yungsters

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.
Projects
None yet
4 participants