Обновление содержимого на экране в React Native приводит к зависанию анимированных компонентов - PullRequest
0 голосов
/ 03 августа 2020

На главном экране моего собственного приложения React есть компоненты, которые анимируются при прокрутке. Однако любое небольшое изменение на экране сбрасывает компоненты в их состояние по умолчанию, и они больше не анимируются.

Я пытаюсь получить данные и отобразить их на главном экране. При загрузке нового содержимого анимированные компоненты go возвращаются в исходное состояние.

home / index.tsx

import React, { useEffect } from 'react'
import { View, StyleSheet, StatusBar, Image } from 'react-native'
import Animated, { Extrapolate } from 'react-native-reanimated'
import { useDispatch, useSelector } from 'react-redux'
import { bannerImage, logoImage } from '../../assets/images'
import CategoryContainer from './CategoryContainer'
import colors from '../../styles/colors'
import CstmBigDisplayButton from '../../components/CstmBigDisplayButton'
import globalStyles from '../../styles/globals'
import MenuButton from '../../components/MenuButton'
import TranslucentStatusBar from '../../components/TranslucentStatusBar'
import { getCategories } from '../../redux/actions/categoryAction'
import { RootState } from '../../redux/types'

const HEADER_MAX_HEIGHT = 430
const HEADER_MIN_HEIGHT = 100

const Home = ({ navigation }: any) => {
    const { categories } = useSelector((state: RootState) => state.categories)
    const dispatch = useDispatch()
    useEffect(() => {
        dispatch(getCategories())
    }, [])
    let scrollY = new Animated.Value(0)
    const headerHeight = Animated.interpolate(
        scrollY,
        {
            inputRange: [0, HEADER_MAX_HEIGHT],
            outputRange: [HEADER_MAX_HEIGHT, HEADER_MIN_HEIGHT],
            extrapolate: Extrapolate.CLAMP,
        }
    )
    const animateOverlay = Animated.interpolate(
        scrollY,
        {
            inputRange: [0, HEADER_MAX_HEIGHT / 2, HEADER_MAX_HEIGHT - 30],
            outputRange: [1, 0.5, 0.1],
            extrapolate: Extrapolate.CLAMP,
        }
    )
    const menuOpacity = Animated.interpolate(
        scrollY,
        {
            inputRange: [0, HEADER_MAX_HEIGHT - 30],
            outputRange: [0.5, 1],
            extrapolate: Extrapolate.CLAMP
        }
    )

    return (
    <>
        <TranslucentStatusBar />
        <Animated.View
            style={[
                styles.header,
                {
                    height: headerHeight,
                    elevation: 10
                }
            ]}
        >
            <View style={styles.headerBackground} >
                <Animated.View
                    style={{
                        position: 'absolute',
                        top: 0,
                        right: 0,
                        zIndex: 11,
                        marginTop: StatusBar.currentHeight,
                        opacity: menuOpacity,
                    }}
                >
                    <View style={{ margin: 16 }}>
                        <MenuButton onPress={() => {navigation.openDrawer()}}/>
                    </View>
                </Animated.View>
                <Animated.Image
                    source={bannerImage}
                    resizeMode='cover'
                    style={{
                        position: 'absolute',
                        left: 0,
                        right: 0,
                        top: 0,
                        height: headerHeight,
                        opacity: animateOverlay,
                }}/>
                <Animated.View
                    style={[ 
                        styles.overlay,
                        {
                            backgroundColor: animateOverlay
                        }
                    ]}
                >
                    <Image
                        source={logoImage}
                        style={styles.logo}
                        resizeMode='contain'
                    />
                </Animated.View>
            </View>
        </Animated.View>
        <Animated.ScrollView
            scrollEventThrottle={16}
            style={globalStyles.screenDefaults}
            onScroll={Animated.event(
                [{ nativeEvent: { contentOffset: { y: scrollY } } }],
                {
                    useNativeDriver: true,
                    listener: (event: any) => console.log(event)
                }
            )}
        >
            <View style={styles.contentContainer}>
                <CategoryContainer
                    titleStyle={[styles.general]}
                    titleText='General Categories'
                    titleTextStyle={{ color: colors.primary["500TextColor"] }}
                    categories={categories.filter((category: any) => !category.special)}
                    navigation={navigation}
                />
                <View style={styles.divider}></View>
                <CategoryContainer
                    titleStyle={[styles.special]}
                    titleText='Special Categories'
                    titleTextStyle={{ color: colors.secondary["700TextColor"] }}
                    categories={categories.filter((category: any) => category.special)}
                    navigation={navigation}
                />
            </View>
            <CstmBigDisplayButton />
        </Animated.ScrollView>
    </>
    )
}

const styles = StyleSheet.create({
    container: {
        flex: 1
    },
    header: {
        position: 'absolute',
        left: 0,
        right: 0,
        top: 0,
        height: HEADER_MAX_HEIGHT,
        backgroundColor: 'grey',
        zIndex: 10,
        alignContent: 'center',
        justifyContent: 'center'
    },
    headerBackground: {
        width: '100%',
        flex: 1,
        backgroundColor: '#FFF'
    },
    logo: {
        flex: 1,
        height: undefined,
        width: undefined,
    },
    overlay: {
        flex: 1,
        paddingTop: StatusBar.currentHeight
    },
    contentContainer: {
        flexDirection: 'row',
        flex: 1,
        paddingTop: HEADER_MAX_HEIGHT,
        paddingBottom: 24
    },
    general: {
        backgroundColor: colors.primary[500],
        color: colors.primary["500TextColor"]
    },
    special: {
        backgroundColor: colors.secondary[700],
        color: colors.secondary["700TextColor"]
    },
    divider: {
        backgroundColor: '#909090',
        height: '99%',
        width: '0.1%'
    },
    
})

export default Home

globals.ts

import { StyleSheet, StatusBar } from 'react-native' 
import theme, { standardInterval } from './theme'

const globalStyles = StyleSheet.create({
  gutterBottom: {
    marginBottom: 8
  },
  captionText: {
    fontSize: 12
  },
  screenDefaults: {
    flex: 1,
        backgroundColor: theme.pageBackgroundColor,
  },
  headlessScreenDefaults: {
        paddingTop: StatusBar.currentHeight,
        padding: 16
    },
  iconText: {
    flexDirection: 'row',
    alignItems: 'center'
  },
  iconTextMargin: {
    marginLeft: 4
  },
  label: {
    fontSize: 16,
    marginBottom: 4,
    paddingLeft: 12,
    color: '#555'
  },
  textInput: {
    padding: 0,
    fontSize: 16,
    color: '#444',
    marginLeft: 6,
    marginRight: 8,
    flex: 1
  },
  textInputContainer: {
    borderWidth: 1,
    borderColor: '#ddd',
    backgroundColor: 'white',
    borderRadius: standardInterval(0.5),
    padding: 8,
    flexDirection: 'row',
    alignItems: 'center'
  },
  helperText: {
    marginTop: 4,
    paddingLeft: 12,
    fontSize: 12,
    color: '#555'
  },
  button: {
    padding: standardInterval(1.5),
    borderRadius: standardInterval(.5),
    // marginLeft: 12,
  },
})

export default globalStyles

Стоит отметить. Когда контент извлекается из компонента навигации по выдвижному ящику, анимированные компоненты работают нормально. Это ненадежно, поскольку, если данные получены, когда домашний экран уже смонтирован, компоненты не будут анимированы.

DrawerNavigation.tsx

import React, { useEffect } from 'react'
import { createDrawerNavigator, DrawerContentScrollView, DrawerItemList, DrawerItem, DrawerContentComponentProps,  } from '@react-navigation/drawer'
import { NavigationContainer } from '@react-navigation/native'
import { useSelector, useDispatch } from 'react-redux'
import AuthNavigator from './AuthNavigator'
import MainNavigator from './MainNavigator'
import theme from '../styles/theme'
import colors from '../styles/colors'
import Profile from '../screens/profile'
import { RootState } from '../redux/types'
import { verifyToken } from '../redux/actions/authAction'
import Splash from '../screens/splash'
import { logOut } from '../redux/actions/authAction'
import { getMetadata } from '../redux/actions/metadataActions'
import { getCategories } from '../redux/actions/categoryAction'

const Drawer = createDrawerNavigator()

const DrawerNavigator = () => {
  const dispatch = useDispatch()
  const { hasInitiallyLoaded, authenticationToken, authenticatedUser } = useSelector((state: RootState) => state.auth)
  useEffect(() => {
    if (!hasInitiallyLoaded) dispatch(verifyToken(authenticationToken))
  })
  useEffect(() => {
    dispatch(getMetadata())
    dispatch(getCategories())
  }, [getMetadata])
  
  return (
    <>
      {
        hasInitiallyLoaded
          ? <NavigationContainer>
              <Drawer.Navigator
                drawerStyle={{ backgroundColor: theme.pageBackgroundColor }}
                drawerContentOptions={{ activeTintColor: colors.secondary[500] }}
                drawerContent={(props: DrawerContentComponentProps) => (
                  <DrawerContentScrollView {...props}>
                    <DrawerItemList {...props} />
                    {
                      authenticatedUser
                        && <DrawerItem
                              label='log out'
                              onPress={() => {dispatch(logOut([() => props.navigation.navigate('home')]))}}
                            />
                    }
                  </DrawerContentScrollView>
                )}
              >
                <Drawer.Screen name='home' component={MainNavigator} />
                {
                  authenticatedUser
                    ? <Drawer.Screen name='profile' component={Profile} />
                    : <Drawer.Screen name='login' component={AuthNavigator} />
                }
              </Drawer.Navigator>
            </NavigationContainer>
          : <Splash />
      }
    </>
  )
}

export default DrawerNavigator

Кроме того, слушатель в Animated.event() в Animated.ScrollView не работает

Animated.event(
[{ nativeEvent: { contentOffset: { y: scrollY } } }],
    {
        useNativeDriver: true,
        listener: (event: any) => console.log(event)
    }
)}

1 Ответ

0 голосов
/ 19 августа 2020

После помещения представления scrollY в состояние все работает, как ожидалось.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...