software-mansion / react-native-reanimated

React Native's Animated library reimplemented
https://docs.swmansion.com/react-native-reanimated/
MIT License
9.05k stars 1.31k forks source link

Laggy Layout when using Animated.View with entering animations as a child. #2929

Closed iSued closed 2 years ago

iSued commented 2 years ago

I am using entering animations and exiting animations in an

<Animated.View entering={FadeInLeft} exiting={FadeOutLeft}>
          {ActiveSearchType.SERIE === activeSearchType && <SerieResultList />}
</Animated.View>

this component is child of a SafeAreaView from 'react-native-safe-area-context' library.

import {
  Pressable,
  SafeAreaView,
  StatusBar,
  StyleSheet,
  Text,
  TextInput,
  View,
  Keyboard,
} from 'react-native';
import {useRecoilValue, useSetRecoilState} from 'recoil';
import {
  videoSearchResult,
  serieSearchResult,
} from '../../Recoil/SearchPage/SearchPage';
import {
  activeCompanySkin,
  hideSearchVideo,
} from '../../Recoil/CompanyDetails/ActiveCompany';
import {searchSeries} from '../../network/API/API_serie';
import {searchVideos} from '../../network/API/API_video';
import {strings} from '../../strings/language';
import Animated, {
  color,
  FadeInLeft,
  FadeInRight,
  FadeOutLeft,
  FadeOutRight,
} from 'react-native-reanimated';
import {useForm, Controller} from 'react-hook-form';
import FontAwesome5 from 'react-native-vector-icons/FontAwesome5';
import BottomSheet, {
  BottomSheetBackdrop,
  BottomSheetFlatList,
  BottomSheetView,
  useBottomSheetDynamicSnapPoints,
  TouchableOpacity,
} from '@gorhom/bottom-sheet';
import {getSearchCategories} from '../../Recoil/SearchPage/SearchPage';
import VideoResultList from '../../components/SearchPageComponents/VideoResulList';
import SerieResultList from '../../components/SearchPageComponents/SerieResultList';

enum ActiveSearchType {
  VIDEO = 'video',
  SERIE = 'serie',
}
const SearchPage = () => {
  const setVideoSearchResult = useSetRecoilState(videoSearchResult);
  const setSerieSearchResult = useSetRecoilState(serieSearchResult);

  const searchCategories = useRecoilValue(getSearchCategories);
  const [selectedCategoryId, setSelectedCategoryId] = useState<number | null>(
    null,
  );
  const [activeSearchType, setActiveSearchType] = useState<ActiveSearchType>(
    ActiveSearchType.SERIE,
  );
  const searchRef: any = React.useRef();
  const Skin = useRecoilValue(activeCompanySkin);
  const Strings = useRecoilValue(strings);
  const {
    _backgroundColor,
    alphaColor,
    fontColor,
    primaryColor,
    buttonTextColor,
  } = Skin;
  //ENIVRONMENT
  const {
    control,
    handleSubmit,
    formState: {errors},
  } = useForm({
    defaultValues: {
      search: '',
    },
  });
  const onSubmit = async ({search}: {search: string}) => {
    console.log(search);
    const searchPromises = await Promise.all([
      searchSeries(
        1,
        search,
        selectedCategoryId !== null ? [selectedCategoryId] : [],
      ),
      searchVideos(
        1,
        search,
        selectedCategoryId !== null ? [selectedCategoryId] : [],
      ),
    ]);
    setSerieSearchResult(searchPromises[0]);
    setVideoSearchResult(searchPromises[1]);
  };
  React.useEffect(() => {
    searchRef?.current.focus();

    const showSubscription = Keyboard.addListener('keyboardDidShow', () => {
      bottomSheetRef?.current?.close();
    });
    return () => {
      showSubscription.remove();
    };
  }, []);
  //BOTTOMSHEET_
  const bottomSheetRef = React.useRef<BottomSheet>(null);
  const data = useMemo(() => searchCategories, []);
  const initialSnapPoints = useMemo(() => ['25%', 'CONTENT_HEIGHT'], []);

  const {
    animatedHandleHeight,
    animatedSnapPoints,
    animatedContentHeight,
    handleContentLayout,
  } = useBottomSheetDynamicSnapPoints(initialSnapPoints);
  const handleSheetChanges = useCallback((index: number) => {
    if (index === 1 || index === 0) {
      Keyboard.dismiss();
    } else {
    }
  }, []);
  const renderBackdrop = useCallback(
    props => (
      <BottomSheetBackdrop
        {...props}
        disappearsOnIndex={-1}
        appearsOnIndex={1}
      />
    ),
    [],
  );
  const SwipeableResultList = () => {
    return (
      <View style={{flex: 1, marginTop: 40}}>
        <Animated.View entering={FadeInLeft} exiting={FadeOutLeft}>
          {ActiveSearchType.SERIE === activeSearchType && <SerieResultList />}
        </Animated.View>
        <Animated.View entering={FadeInRight} exiting={FadeOutRight}>
          {ActiveSearchType.VIDEO === activeSearchType && <VideoResultList />}
        </Animated.View>
      </View>
    );
  };

  return (
    <>
      <StatusBar
        animated={true}
        backgroundColor={_backgroundColor}
        barStyle={fontColor}
      />

      <SafeAreaView
        style={{
          flex: 1,
          backgroundColor: _backgroundColor,
          flexDirection: 'column',
          padding: 15,
        }}>
        <View>
          <Controller
            control={control}
            render={({field: {onChange, onBlur, value}}) => (
              <View
                style={[
                  styles.searchInputContainer,
                  {
                    borderColor: fontColor,
                  },
                ]}>
                <View
                  style={{
                    flex: 4,
                  }}>
                  <TextInput
                    style={[
                      styles.input,
                      {
                        color: fontColor,
                      },
                    ]}
                    returnKeyType={'search'}
                    ref={searchRef}
                    onBlur={onBlur}
                    onChangeText={onChange}
                    value={value}
                    placeholder={Strings.SearchPlaceholder}
                    placeholderTextColor="#A9A9AC"
                    autoCapitalize={'none'}
                    autoCorrect={false}
                    onSubmitEditing={handleSubmit(onSubmit)}
                  />
                </View>
                <View
                  style={{
                    flex: 2,
                    justifyContent: 'center',
                    alignItems: 'center',
                  }}>
                  <Pressable
                    onPress={() => {
                      bottomSheetRef?.current?.expand();
                    }}
                    style={({pressed}) => [
                      {
                        flex: 1,
                        justifyContent: 'center',
                        alignItems: 'center',
                        backgroundColor: _backgroundColor,
                        borderWidth: 2,
                        borderColor: fontColor,
                        borderRadius: 30,
                        width: '100%',
                        height: '100%',
                        opacity: pressed ? 0.7 : 1,
                      },
                    ]}>
                    <View
                      style={{
                        flexDirection: 'row',
                        justifyContent: 'center',
                        alignItems: 'center',
                      }}>
                      <Text
                        style={{
                          color: buttonTextColor,
                          fontSize: 14,
                          marginRight: 5,
                        }}>
                        {Strings.SearchCategory}
                      </Text>
                      <FontAwesome5
                        name={'chevron-down'}
                        solid
                        color={fontColor}
                        size={14}
                      />
                    </View>
                  </Pressable>
                </View>
                <View
                  style={{
                    flex: 1,
                    justifyContent: 'center',
                    alignItems: 'center',
                  }}>
                  <FontAwesome5
                    name={'search'}
                    solid
                    color={fontColor}
                    size={24}
                  />
                </View>
              </View>
            )}
            name="search"
          />
        </View>
        <View
          style={{
            marginTop: 20,

            flexDirection: 'row',
          }}>
          <Pressable
            onPress={() => {
              setActiveSearchType(ActiveSearchType.SERIE);
            }}
            style={{
              flex: 3,
              borderWidth: 2,
              borderColor:
                activeSearchType === ActiveSearchType.SERIE
                  ? primaryColor
                  : fontColor,
              backgroundColor:
                activeSearchType === ActiveSearchType.SERIE
                  ? primaryColor
                  : _backgroundColor,
              borderRadius: 20,
              paddingVertical: 5,
              justifyContent: 'center',
              alignItems: 'center',
            }}>
            <Text
              style={{
                color:
                  activeSearchType === ActiveSearchType.SERIE
                    ? buttonTextColor
                    : fontColor,
                textAlign: 'center',
              }}>
              Serie
            </Text>
          </Pressable>
          <Pressable
            onPress={() => {
              setActiveSearchType(ActiveSearchType.VIDEO);
            }}
            style={{
              flex: 3,
              borderWidth: 2,
              borderColor:
                activeSearchType === ActiveSearchType.VIDEO
                  ? primaryColor
                  : fontColor,
              backgroundColor:
                activeSearchType === ActiveSearchType.VIDEO
                  ? primaryColor
                  : _backgroundColor,
              borderRadius: 20,
              paddingVertical: 5,
              justifyContent: 'center',
              alignItems: 'center',
            }}>
            <Text
              style={{
                color:
                  activeSearchType === ActiveSearchType.VIDEO
                    ? buttonTextColor
                    : fontColor,
                textAlign: 'center',
              }}>
              Video
            </Text>
          </Pressable>
        </View>
        <SwipeableResultList />
        <BottomSheet
          ref={bottomSheetRef}
          index={-1}
          snapPoints={animatedSnapPoints}
          handleHeight={animatedHandleHeight}
          contentHeight={animatedContentHeight}
          enablePanDownToClose={true}
          backdropComponent={renderBackdrop}
          onChange={handleSheetChanges}
          handleStyle={{backgroundColor: fontColor}}
          backgroundStyle={{backgroundColor: fontColor}}>
          <BottomSheetView
            style={{
              flex: 1,
              backgroundColor: _backgroundColor,
              padding: 6,
              margin: 6,
            }}
            onLayout={handleContentLayout}>
            <BottomSheetFlatList
              style={{width: '100%'}}
              data={data}
              keyExtractor={(i: any) => i.name_ml}
              renderItem={({item}: {item: any}) => {
                return (
                  <TouchableOpacity
                    onPress={() => {
                      setSelectedCategoryId(item.id);
                      bottomSheetRef.current?.close();
                    }}>
                    <View
                      style={{
                        flex: 1,
                        backgroundColor: _backgroundColor,
                        padding: 6,
                        margin: 6,
                        flexDirection: 'row',
                      }}>
                      <View
                        style={{
                          flex: 0.5,
                          justifyContent: 'flex-end',
                          alignItems: 'center',
                        }}>
                        <View
                          style={{
                            height: 16,
                            width: 16,
                            borderWidth: 2,
                            borderRadius: 30,
                            borderColor: fontColor,
                            justifyContent: 'center',
                            alignItems: 'center',
                          }}>
                          <View
                            style={{
                              display:
                                selectedCategoryId === item.id
                                  ? 'flex'
                                  : 'none',
                              backgroundColor: fontColor,
                              height: 10,
                              width: 10,
                              borderRadius: 30,
                            }}
                          />
                        </View>
                      </View>
                      <View
                        style={{
                          flex: 5.5,
                          alignItems: 'center',
                        }}>
                        <Text
                          style={{
                            width: '100%',
                            textAlign: 'left',
                            color: fontColor,
                          }}>
                          {item.name}
                        </Text>
                      </View>
                    </View>
                  </TouchableOpacity>
                );
              }}
            />
          </BottomSheetView>
        </BottomSheet>
      </SafeAreaView>
    </>
  );
};
const styles = StyleSheet.create({
  input: {},
  searchInputContainer: {
    borderWidth: 2,
    borderRadius: 30,
    padding: 10,
    flexDirection: 'row',
  },
});
export default SearchPage;

When using animations in entering on any of the views inside the layout is not behaving as expected. The Layout becomes laggy moving up in a non displayed area, under the navigation Header

Expected behavior

Should behave as when i'm not using entering animations.

Actual behavior & steps to reproduce

Layout problems pushing my View and making it laggy

Snack or minimal code example

Package versions

name version
react-native 0.66.4
react-native-reanimated ^2.3.1
NodeJS v16.9.1.
Xcode 13.2.1
Java java version "15" 2020-09-15

Affected platforms

github-actions[bot] commented 2 years ago

Hey! đŸ‘‹

It looks like you've omitted a few important sections from the issue template.

Please complete Description and Snack or minimal code example sections.

Only-IceSoul commented 2 years ago

I think reanimated is tested on a powerful computer and a powerful simulator or cell phone. It is better that you do your animations.

iSued commented 2 years ago

I think reanimated is tested on a powerful computer and a powerful simulator or cell phone. It is better that you do your animations.

I'm testing animations using two top devices. Your answer doesn't make sense. I think i found the problem. I'ill update the issue.

KrasniqiR commented 2 years ago

Hey, did you figure out the solution to this? I'm experiencing something similar where other views are affected just by the presence of an Animated View.

iSued commented 2 years ago

I forgot to update the issu because i was very busy. The problem was the react-native version.

I had 0.66.4 RN version and 2.4.1 of Reanimated. Updating RN to 0.67.3 the problem was solved.

So basically when installing a version of Reanimated be sure to check which version of RN there's in Reanimated Package.json

iSued commented 2 years ago

UPDATE the real bug was the header height property. It must be set to avoid glitch.

Because under the hood React-Native-Navigation mounts Reanimated 2.

hellostu commented 2 years ago

UPDATE the real bug was the header height property. It must be set to avoid glitch.

Because under the hood React-Native-Navigation mounts Reanimated 2.

Hello, would you mind providing code to show exactly what you mean in how you addressed it with header height? Do you mean:

headerStyle: {
   height: 80
}

As i'm seeing this issue and it didn't address it for me. We are currently on RN v66.4 though.

iSued commented 2 years ago

I ned to be more specific. Basically at the end i ended up with custom headers with a specific height:number not percentage or flexbox. So every screen renders a custom header, and this worked quite fine for me.

When using specific height with default header with custom components inside, it used to be glitchy, it seems that default header works fine only when changing little stuff like title, or back button.

For example: I had a logo that i need to show in every page in the center of the header and this was causing a strange behaviour