Skip to content

Update initial style every render#2851

Merged
piaskowyk merged 12 commits intomainfrom
@piaskowyk/initial-style-shared
Mar 18, 2022
Merged

Update initial style every render#2851
piaskowyk merged 12 commits intomainfrom
@piaskowyk/initial-style-shared

Conversation

@piaskowyk
Copy link
Member

@piaskowyk piaskowyk commented Jan 17, 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:

code

Before

Screen.Recording.2022-03-14.at.10.22.09.mov
Screen.Recording.2022-03-14.at.12.54.58.mov

After

Screen.Recording.2022-01-17.at.16.41.55.mov
code
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>
  );
}

code2

Still works

Screen.Recording.2022-01-17.at.16.44.45.mov
code2
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;

@piaskowyk piaskowyk self-assigned this Jan 17, 2022
@hannojg
Copy link
Contributor

hannojg commented Jan 26, 2022

just wanted to add that i tested this PR in our app and its working fine 🎊

@hirbod
Copy link
Contributor

hirbod commented Feb 3, 2022

This PR is what I was searching for. It fixes plenty of issues for me, specially with react-native-bottom-sheet.
Hopefully this is going to land quickly. Good catch and nice work!

Edit:
I can also confirm that this fixed some issues with moti as well.

@daavidkllr
Copy link

daavidkllr commented Feb 3, 2022

Same here, this will fix a bunch of bugs I've investigated.
Please do not let us wait too long for this 🙂

@hirbod
Copy link
Contributor

hirbod commented Feb 10, 2022

@piaskowyk any chance to get more traction on this PR here? Or do you expect for it to take some more time?
If so, I would create a patch for it on my local build.

@piaskowyk
Copy link
Member Author

piaskowyk commented Feb 22, 2022

#2580 finally was merged, so I can deal with this now.

@piaskowyk
Copy link
Member Author

I want to release these changes in 2.5.0

@piaskowyk piaskowyk closed this Feb 23, 2022
@piaskowyk piaskowyk force-pushed the @piaskowyk/initial-style-shared branch from 8a3d77b to 164ba30 Compare February 23, 2022 08:11
@piaskowyk piaskowyk reopened this Feb 23, 2022
@hirbod
Copy link
Contributor

hirbod commented Mar 3, 2022

@piaskowyk when do you think 2.5.0 will be out? We're desperately waiting for this PR to land.
This is really needed for a bunch of libs to work correctly.

Or could you create a pre-release already?

@piaskowyk
Copy link
Member Author

@hirbod I worked on this issue, and I discovered a serious problem with the order of mapper execution. In other words, there is a possibility to perform updates on not fully rendered components. I've been talking to @kmagiera bout this PR and we decided that this is just a fix of effect not a source of the problem. Even if I release these changes it can be harmful in some cases, and we don't want to add regression. So we decided to fix it on the native side. I have some unpublished prototypes but I need some time to polish them. At this moment I am only one maintainer of reanimated so that's why it's taking so long - sorry for that.

@hirbod
Copy link
Contributor

hirbod commented Mar 4, 2022

@piaskowyk thank you very much for the update. I understand your concerns. Personally speaking, I haven't had any regressions so far but good thing that you've catched them (I am on 2.3.1 and using a patch-file to add your changes), and it fixes bunch of issues with bottom-sheet and moti for me. As you can see, there are plenty of issues linking to this here and the operations order issue.

So I will stick with the patch for now and hope that those native fixes will tackle the core problem.
If I can help you out with testing, give me a shout!

@hirbod
Copy link
Contributor

hirbod commented Mar 7, 2022

@piaskowyk I can also confirm regressions now. So good thing that you've catched it.

@piaskowyk piaskowyk marked this pull request as ready for review March 14, 2022 09:13
@piaskowyk
Copy link
Member Author

@hirbod I found an easier solution, could you check if it works for you?

@hirbod
Copy link
Contributor

hirbod commented Mar 14, 2022

@piaskowyk shall I install @piaskowyk/initial-style-shared or this PR directly?

@piaskowyk
Copy link
Member Author

Both should work the same.

@hirbod
Copy link
Contributor

hirbod commented Mar 14, 2022

@piaskowyk in all of my tests, the bugs with react-native-bottom-sheet, react-native-vision-camera and moti are gone. The regressions I've encountered are gone as well.

From my end, this PR looks rock solid!

@piaskowyk
Copy link
Member Author

piaskowyk commented Mar 14, 2022

Thanks for the help ❤️

@piaskowyk piaskowyk requested a review from tomekzaw March 17, 2022 17:09
@piaskowyk piaskowyk merged commit 86546e2 into main Mar 18, 2022
@piaskowyk piaskowyk deleted the @piaskowyk/initial-style-shared branch March 18, 2022 09:53
@tr3v3r
Copy link

tr3v3r commented Mar 19, 2022

@tomekzaw hi! Just writing to enquire when the release with this update is planned? Thanks

@tomekzaw
Copy link
Member

@tr3v3r We plan to release 2.5.0 on Monday. In the meantime, you can try out this version built on our CI.

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.

6 participants