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

[v3] Performance issue since migration from 2.0 -> 3.0 #398

Closed
samducker opened this issue Dec 5, 2024 · 10 comments · Fixed by #403
Closed

[v3] Performance issue since migration from 2.0 -> 3.0 #398

samducker opened this issue Dec 5, 2024 · 10 comments · Fixed by #403

Comments

@samducker
Copy link

samducker commented Dec 5, 2024

Description

So I followed the migration guide from unistyles 2.0 -> 3.0

I have this tab button which opens a bottom sheet, and since I moved over to the new version it renders extremely slowly, before it worked almost instantly now it is noticeably choppy.

Is there something I am doing incorrectly in my approach, my new code is below.

I also sometimes get Style is not bound error! but I'm not sure why, as I don't see where Im merging styles anywhere.

(tabs)/_layout.tsx

import { Tabs } from 'expo-router';
import FontAwesome from '@expo/vector-icons/FontAwesome';

const ThemedTabs = createUnistylesComponent(Tabs, theme => ({
  screenOptions: {
    tabBarActiveTintColor: theme.colors.surface['900'],
    tabBarInactiveTintColor: theme.colors.surface[100],
    tabBarStyle: {
      backgroundColor: theme.colors.surface['000'],
      borderTopWidth: 0,
      height: 90,
    },
  },
}));

const ThemedFontAwesome = createUnistylesComponent(FontAwesome, theme => ({
  color: theme.colors.surface['900'],
}));

<ThemedTabs>
... other tabs
<Tabs.Screen
        name="plus"
        options={{
          headerShown: false,
          tabBarShowLabel: false,
          tabBarIcon: ({ color }) => <FontAwesome size={28} name={'plus'} color={color} />,
          tabBarButton: () => {
            return (
              <GenericBottomSheet
                snapPoints={['45%']}
                triggerComponent={
                  <View
                    style={{
                      display: 'flex',
                      paddingHorizontal: 25,
                      height: '100%',
                      borderRadius: 10,
                      justifyContent: 'center',
                      alignItems: 'center',
                    }}
                  >
                    <View style={styles.plusButton}>
                      <ThemedFontAwesome size={24} name={'plus'} />
                    </View>
                  </View>
                }
              >
                <View style={styles.bottomSheetContent}>
                  <BottomSheetNavigationItem
                    path="/(auth)/log-meal"
                    emoji={'greenApple'}
                    color={'red'}
                    title="Log a meal"
                    description="Add a meal to your diary"
                  />
                  <BottomSheetNavigationItem
                    path="/(auth)/log-workout"
                    emoji={'weightLifting'}
                    color={'blue'}
                    title="Log a workout"
                    description="Add a workout to your diary"
                  />
                  <BottomSheetNavigationItem
                    path="/(auth)/log-progress"
                    color={'green'}
                    emoji={'ruler'}
                    title="Track progress"
                    description="Update weight or measurements"
                  />
                </View>
              </GenericBottomSheet>
            );
          }
/>
</ThemedTabs>

generic-bottom-sheet.tsx

import React, {
  useCallback,
  useRef,
  ReactNode,
  ReactElement,
  Children,
  isValidElement,
  cloneElement,
} from 'react';
import { View, TouchableOpacity } from 'react-native';
import {
  BottomSheetModal,
  BottomSheetBackdrop,
  BottomSheetBackdropProps,
  BottomSheetScrollView,
  BottomSheetView,
} from '@gorhom/bottom-sheet';
import { StyleSheet } from 'react-native-unistyles';

type Props = {
  children: ReactNode;
  triggerComponent: ReactNode;
  snapPoints?: (string | number)[];
  onChange?: (index: number) => void;
  scrollable?: boolean;
};

const DEFAULT_SNAP_POINTS = ['50%', '90%'];

export default function GenericBottomSheet({
  children,
  triggerComponent,
  snapPoints = DEFAULT_SNAP_POINTS,
  onChange,
  scrollable = false,
}: Props) {
  const bottomSheetModalRef = useRef<BottomSheetModal>(null);

  const renderBackdrop = useCallback(
    (props: BottomSheetBackdropProps) => (
      <BottomSheetBackdrop {...props} appearsOnIndex={0} disappearsOnIndex={-1} />
    ),
    []
  );

  const handlePress = () => {
    bottomSheetModalRef.current?.present();
  };

  const handleSheetChanges = useCallback(
    (index: number) => {
      if (onChange) {
        onChange(index);
      }
    },
    [onChange]
  );

  const dismiss = () => {
    bottomSheetModalRef.current?.dismiss();
  };

  const renderChildrenWithDismiss = (children: ReactNode): ReactNode => {
    return Children.map(children, child => {
      if (isValidElement(child)) {
        return cloneElement(child as ReactElement, {
          dismiss,
          children: renderChildrenWithDismiss((child as ReactElement).props.children),
        });
      }
      return child;
    });
  };

  return (
    <View>
      <TouchableOpacity onPress={handlePress} activeOpacity={0.8}>
        {triggerComponent}
      </TouchableOpacity>
      <BottomSheetModal
        ref={bottomSheetModalRef}
        snapPoints={snapPoints}
        onChange={handleSheetChanges}
        backdropComponent={renderBackdrop}
        backgroundStyle={styles.backgroundStyle}
        handleIndicatorStyle={styles.handleIndicator}
      >
        {scrollable ? (
          <BottomSheetScrollView style={styles.container}>
            {renderChildrenWithDismiss(children)}
          </BottomSheetScrollView>
        ) : (
          <BottomSheetView style={styles.container}>
            {renderChildrenWithDismiss(children)}
          </BottomSheetView>
        )}
      </BottomSheetModal>
    </View>
  );
}

const styles = StyleSheet.create(theme => ({
  container: {
    paddingHorizontal: 16,
    backgroundColor: theme.colors.surface['000'],
  },
  backgroundStyle: {
    backgroundColor: theme.colors.surface['000'],
  },
  handleIndicator: {
    backgroundColor: theme.colors.surface['300'],
  },
}));

bottom-sheet-navigation-item.tsx

import { Text, TouchableOpacity, View } from 'react-native';
import React from 'react';
import { StyleSheet } from 'react-native-unistyles';
import { Href, router } from 'expo-router';
import typography from '@/constants/typography';
import { EmojiKey } from '@/constants/emojis/types';
import { ColorName } from '@/constants/colors/types';
import { getColor } from '@/constants/colors/utils';
import { getEmojiByKey } from '@/constants/emojis/utils';

type Props = {
  path: Href;
  title: string;
  description: string;
  emoji: EmojiKey;
  color: ColorName;
  dismiss?: () => void;
};

export default function BottomSheetNavigationItem({
  path,
  title,
  color,
  emoji,
  description,
  dismiss,
}: Props) {
  const colorHex = getColor(color);
  const emojiCode = getEmojiByKey(emoji);

  return (
    <TouchableOpacity
      style={styles.container}
      onPress={() => {
        if (dismiss) {
          dismiss();
        }
        router.push(path);
      }}
      activeOpacity={0.8}
    >
      <View style={styles.iconContainer(colorHex)}>
        <Text style={styles.emoji}>{emojiCode}</Text>
      </View>
      <View style={styles.textContainer}>
        <Text style={styles.title}>{title}</Text>
        <Text style={styles.description}>{description}</Text>
      </View>
    </TouchableOpacity>
  );
}

const styles = StyleSheet.create(theme => ({
  container: {
    display: 'flex',
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    padding: theme.padding.md,
    borderBottomWidth: 1,
    gap: 20,
    borderBottomColor: theme.colors.surface['020'],
  },
  emoji: {
    fontSize: 28,
  },

  iconContainer: (color: string) => ({
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    width: 50,
    height: 50,
    borderRadius: 10,
    backgroundColor: color,
  }),

  textContainer: {
    display: 'flex',
    flexDirection: 'column',
    flex: 1,
    alignItems: 'flex-start',
    justifyContent: 'center',
  },
  title: {
    ...typography.headings.h4,
    color: theme.colors.text['900'],
  },
  description: {
    ...typography.body.bodySmall,
    color: theme.colors.text['500'],
  },
}));

Steps to Reproduce

Untested on android

Snack or Repository Link (Optional)

No response

Unistyles Version

3.0.0

React Native Version

0.76.3

Platforms

iOS

Expo

Yes

@samducker samducker changed the title Performance issue since migration from 2.0 -> 3.0 [v3] Performance issue since migration from 2.0 -> 3.0 Dec 5, 2024
@jpudysz
Copy link
Owner

jpudysz commented Dec 6, 2024

I don't see anything specific. Can you give me some repro so I can play around?

@samducker
Copy link
Author

samducker commented Dec 6, 2024

Hey @jpudysz

I've made a minimal demo repo here with a fresh expo install, here is the link https://github.com/houseofduck/react-native-unistyles-perf-issues-minimal-demo

I managed to not get the style not bound error at one point, but it was still choppy.

Sorry in advance if I have made a silly mistake somewhere!

To recreate the issue press the plus button in the bottom tabs, and you should be able to see the changes from a fresh expo install in the commit history.

@jpudysz
Copy link
Owner

jpudysz commented Dec 6, 2024

Thanks and no worries.
Will definitely take a look before releasing beta.2!

@jpudysz jpudysz added this to the 3.0.0-beta.2 milestone Dec 6, 2024
@jpudysz
Copy link
Owner

jpudysz commented Dec 9, 2024

Is it ok now? 😄

RocketSim_Recording_iPhone_16_Pro_Max_6.9_2024-12-09_07.26.35.mp4

@samducker
Copy link
Author

Thank you!

@samducker
Copy link
Author

Hey @jpudysz hope you are well sorry to tag but this issue is currently closed.

the issue is improved for me in 3.0.0 beta 2 but its still very low fps compared to unistyles 2.0.0 is there a some debugging steps I can do to provide you more information.

@jpudysz
Copy link
Owner

jpudysz commented Dec 14, 2024

Hey, how do you know it has a lower FPS? Does it look like the video I posted above?
How does it look like?

@samducker
Copy link
Author

Hey @jpudysz, sorry to be likely surfacing errors unrelated to your project more than once now 😭.

After reading the following I believe it may just be a new architecture issue with react native bottom sheet.

Issue on gorham bottom sheet package

RPReplay_Final1734203959.MP4

This is a video from my device, although I will note it looks noticeably choppier on the device than this video for whatever reason.

It just basically has some quite noticeable lag when opening the bottom sheet, according to the issue it is affecting multiple packages.

The changes you previously made has made an improvement however! But assume we can keep this closed.

@jpudysz
Copy link
Owner

jpudysz commented Dec 15, 2024

Ah! Thanks for the reference. That’s what I though as I don’t apply any animations 😇

@rileysay
Copy link

@samducker yeah experiencing the same behaviour also not as noticeable in recordings. Most likely issue with reanimated

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants