Initial style for component with shared style#2431
Merged
Conversation
Szymon20000
approved these changes
Sep 24, 2021
piaskowyk
added a commit
that referenced
this pull request
Mar 18, 2022
## Description We can't use just initial style as the default style in every render because these styles can be outdated. We can't change the default style after the first render, because after the second render we don't run mapper that's why the component can change the style to the initial value. Related: - #2580 - #2431 - #2406 - #1470 ### code Before https://user-images.githubusercontent.com/36106620/158142874-a11191e7-c0d9-4c3f-8f18-e5b540a6f17c.mov https://user-images.githubusercontent.com/36106620/158167177-81dfa334-db01-4e04-a234-e1069e8d715b.mov After https://user-images.githubusercontent.com/36106620/149799832-b0c0748d-2d9d-42b9-b9ba-f6492cc1fbf0.mov <details> <summary>code</summary> ```js import Animated, { useSharedValue, withTiming, useAnimatedStyle, Easing, } from 'react-native-reanimated'; import { View, Button } from 'react-native'; import React, { useState } from 'react'; export default function AnimatedStyleUpdateExample(props:any) { const randomWidth = useSharedValue(10); const [counter, setCounter] = useState(0); const [counter2, setCounter2] = useState(0); const [itemList, setItemList] = useState([]); const [toggleState, setToggleState] = useState(false); const config = { duration: 500, easing: Easing.bezier(0.5, 0.01, 0, 1), }; const style = useAnimatedStyle(() => { return { width: withTiming(randomWidth.value, config), }; }); const staticObject = <Animated.View style={[ { width: 100, height: 3, backgroundColor: 'black', margin: 1 }, style, ]} /> const renderItems = () => { let output = [] for(let i = 0; i < counter; i++) { output.push( <Animated.View key={i + 'a'} style={[ { width: 100, height: 3, backgroundColor: 'blue', margin: 1 }, style, ]} /> ) } return output } return ( <View style={{ flex: 1, flexDirection: 'column', marginTop: 30 }}> <Button title="animate" onPress={() => { randomWidth.value = Math.random() * 350; }} /> <Button title="increment counter" onPress={() => { setCounter(counter + 1) }} /> <Button title="add item to static lists" onPress={() => { setCounter2(counter2 + 1) setItemList([...itemList, <Animated.View key={counter2 + 'b'} style={[ { width: 100, height: 3, backgroundColor: 'green', margin: 1 }, style, ]} />]) }} /> <Button title="toggle state" onPress={() => { setToggleState(!toggleState) }} /> <Animated.View style={[ { width: 100, height: 3, backgroundColor: 'orange', margin: 1 }, style, ]} /> {toggleState && <Animated.View style={[ { width: 100, height: 3, backgroundColor: 'black', margin: 1 }, style, ]} />} {toggleState && staticObject} {renderItems()} {itemList} </View> ); } ``` </details> ### code2 Still works https://user-images.githubusercontent.com/36106620/149800303-4c4316aa-7765-4c66-a81a-74489d9f0215.mov <details> <summary>code2</summary> ```js import React from 'react'; import { View } from 'react-native'; import Animated, { useSharedValue, withSpring, useAnimatedStyle, useAnimatedGestureHandler, interpolate, Extrapolate, runOnJS, } from 'react-native-reanimated'; import { PanGestureHandler, PanGestureHandlerGestureEvent, } from 'react-native-gesture-handler'; import { useEffect, useState } from 'react'; function DragAndSnap(): React.ReactElement { const translation = { x: useSharedValue(0), y: useSharedValue(0), }; type AnimatedGHContext = { startX: number; startY: number; }; // run a couple of updates when gesture starts const [counter, setCounter] = useState(0); const makeFewUpdates = () => { let countdown = 100; const doStuff = () => { setCounter(countdown); countdown--; if (countdown > 0) { requestAnimationFrame(doStuff); } }; doStuff(); }; const gestureHandler = useAnimatedGestureHandler< PanGestureHandlerGestureEvent, AnimatedGHContext >({ onStart: (_, ctx) => { ctx.startX = translation.x.value; ctx.startY = translation.y.value; runOnJS(makeFewUpdates)(); }, onActive: (event, ctx) => { translation.x.value = ctx.startX + event.translationX; translation.y.value = ctx.startY + event.translationY; }, onEnd: (_) => { translation.x.value = withSpring(0); translation.y.value = withSpring(0); }, }); const stylez = useAnimatedStyle(() => { const H = Math.round( interpolate(translation.x.value, [0, 300], [0, 360], Extrapolate.CLAMP) ); const S = Math.round( interpolate(translation.y.value, [0, 500], [100, 50], Extrapolate.CLAMP) ); const backgroundColor = `hsl(${H},${S}%,50%)`; return { transform: [ { translateX: translation.x.value, }, { translateY: translation.y.value, }, ], backgroundColor, }; }); // make render slower let f = 0; for (var i = 0; i < 1e8; i++) { f++; } return ( <View style={{ flex: 1, margin: 50 }}> <PanGestureHandler onGestureEvent={gestureHandler}> <Animated.View style={[ { width: 40, height: 40, }, stylez, ]} /> </PanGestureHandler> </View> ); } export default DragAndSnap; ``` </details>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
In the previous approach, initialUpdaterRun was called inside createAnimatedComponent(), and if
createAnimatedComponentwas called during an animation, it can cause an inconsistent state of style because on UI thread was scheduled to update and on JS thread was computed current style. But on the UI thread, this update can be executed before action on the JS thread or after them.I moved
initialUpdaterRunto createAnimatedComponent and compute initial style in the component's constructor.Fixes: #2406