Skip to content

Fix race-condition during component initialization #7742

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 4 commits into
base: main
Choose a base branch
from

Conversation

piaskowyk
Copy link
Member

@piaskowyk piaskowyk commented Jul 3, 2025

Problem Overview

When a child receives an animated style as a prop from a parent component and the animation is already running, it's possible for heavy components that the view descriptor registration will be called after the animation has finished. As a result, no effect of the animation will be applied, or the component will end up with a state from the middle of the animation.

Solution

This PR modifies the view descriptor's add method such that after the registration of the descriptor, it triggers a force update of the component on the UI. This ensures that the component will have all potentially already computed animation styles applied.

Demo

before after
Screen.Recording.2025-07-14.at.12.26.09.mov
Screen.Recording.2025-07-14.at.12.24.56.mov

Example

code
import React, { useEffect, useState } from 'react';
import { Button, StyleSheet, View, Text } from 'react-native';
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';

function InnerComponent({index, aniamtedStyle}: {index: number, aniamtedStyle: any}) {

  useEffect(() => {
    console.log(Date.now(), 'loop')
    let a = 0;
    for (let i = 0; i < 9999999; i++) {
      a = a * a + 6;
    }
  })

  return <Animated.View 
    style={[
      {width: 100, height: 100, backgroundColor: 'olive', borderColor: 'blue', borderWidth: 3},
      aniamtedStyle
    ]} 
  >
    <Text>{index}</Text>
  </Animated.View>
}

function Wrapper({animation, sv}: {animation: any, sv: any}) {
  const [index, setIndex] = useState(0);
  return <View style={styles.container}>
    <Button title='click' onPress={() => {
      if (sv.value > 150) {
        sv.value = withTiming(100, {duration: 60});
      } else { 
        sv.value = withTiming(300, {duration: 60});
      }
      setIndex(index + 1);
    }} />
    <InnerComponent index={index} key={index} aniamtedStyle={animation} />
    <InnerComponent index={index + 1} key={index + 1} aniamtedStyle={animation} />
  </View>
}

export default function EmptyExample() {
  const width = useSharedValue(100);
  const style = useAnimatedStyle(() => {
    return { width: width.value }
  });
  return (<View style={{marginTop: 60}}>
    <Wrapper animation={style} sv={width}/>
  </View>);
}

const styles = StyleSheet.create({
  container: {},
});

@piaskowyk piaskowyk marked this pull request as ready for review July 14, 2025 10:56
@piaskowyk piaskowyk requested a review from tjzel July 14, 2025 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant