首页 > 解决方案 > 在平面列表中的 react-native scrollToOffset 中无法正常工作?

问题描述

在平面列表中,当项目数小于 3 时,滚动到偏移量无法正常工作。如果项目数大于或等于 3,则工作正常。

import React, { Component } from 'react'
import { Animated, Dimensions, RefreshControl, StyleSheet, View, TouchableOpacity } from 'react-native'
import { HEADER_HEIGHT } from '.'
import { CustomText, FlatListWrapper, FlatListWrapperProps } from '../../../common-library/components'
import { ScrollValueContext } from './Context'
import { get } from 'lodash'
import { CENTER, colors, dimens, ROW, SPACE_BETWEEN } from '../../../common-library/config'
import { TAB_BAR_HEIGHT } from './CommunityHeaderComponent'
import { IconButtonWrapper } from '../generic'
import { icons } from '../../common'
import { log } from '../../config'

const labels = {
    GO_TO_TOP: 'Go to Top'
}

const styles = StyleSheet.create({
    contentStyle: {
        paddingTop: HEADER_HEIGHT
    },
    scrollToTopContainer: {
        position: 'absolute',
        top: TAB_BAR_HEIGHT,
        alignItems: 'center',
        left: 0,
        right: 0,
        zIndex: 999
    },
    buttonsContainer: {
        borderColor: colors.DuckBlue,
        backgroundColor: colors.DuckBlue,
        borderRadius: dimens.size25,
        width: 110,
        flexDirection: ROW,
        justifyContent: SPACE_BETWEEN,
        paddingVertical: dimens.size10,
        alignItems: CENTER
    },
    buttonCta: {
        paddingLeft: dimens.size15
    },
    goToTopLabel: {
        color: colors.White,
        textAlign: CENTER
    },
    crossIconCta: {
        justifyContent: CENTER,
        alignItems: CENTER,
        paddingRight: dimens.size15
        // paddingTop: dimens.size2
    }
})

interface State {
    shouldRefresh?: boolean
    showScrollToTopView?: boolean
    dontShowGoToTop?: boolean
}

interface Props extends FlatListWrapperProps {
    uniqueKey?: string
    getFlatListRef?: (ref) => any
    getScrolledPosition?: (contentOffset) => any
    onPullToRefresh?: () => any
    renderScrollToTopView?: boolean
}

export class StickyScrollableFlatlistComponent extends Component<Props, State> {
    flatListRef
    scrolledValue
    timerRef
    state = {
        shouldRefresh: false,
        showScrollToTopView: false,
        dontShowGoToTop: false
    }

    componentDidUpdate(prevProps) {
        const { showScrollToTopView, dontShowGoToTop } = this.state
        if ((!prevProps.isFetching && this.props.isFetching) || (prevProps.isFetching && !this.props.isFetching)) {
            if ((showScrollToTopView || dontShowGoToTop) && this.props.renderScrollToTopView) {
                this.setState({
                    showScrollToTopView: false,
                    dontShowGoToTop: false
                })
            }
            setTimeout(() => {
                log('setTileout is called', this.scrolledValue > HEADER_HEIGHT, this.scrolledValue)
                this.flatListRef.scrollToOffset({
                    offset: this.scrolledValue > HEADER_HEIGHT ? HEADER_HEIGHT : HEADER_HEIGHT,
                    animated: false
                })
            }, 2000)
        }
    }

    onRefresh = () => {
        const { onPullToRefresh } = this.props
        if (onPullToRefresh) {
            this.setState({
                shouldRefresh: true
            })
            onPullToRefresh().then(() => {
                this.setState({
                    shouldRefresh: false
                })
            })
        }
    }

    showScrollToTopView = () => {
        const { showScrollToTopView } = this.state
        const DEVICE_HEIGHT = Dimensions.get('window').height
        if (this.scrolledValue >= 2 * DEVICE_HEIGHT - HEADER_HEIGHT && !showScrollToTopView) {
            this.setState({
                showScrollToTopView: true
            })
        } else if (this.scrolledValue <= DEVICE_HEIGHT - HEADER_HEIGHT && showScrollToTopView) {
            this.setState({
                showScrollToTopView: false
            })
        }
    }

    onClikCrossIcon = () => {
        this.setState({
            dontShowGoToTop: true,
            showScrollToTopView: false
        })
    }

    moveToTop = () => {
        this.flatListRef.scrollToOffset({
            offset: HEADER_HEIGHT,
            animated: true
        })
    }

    renderScrollToTopView = () => {
        return (
            <View style={styles.scrollToTopContainer}>
                <View style={styles.buttonsContainer}>
                    <TouchableOpacity onPress={this.moveToTop} style={styles.buttonCta} activeOpacity={1}>
                        <CustomText textStyle={styles.goToTopLabel}>{labels.GO_TO_TOP}</CustomText>
                    </TouchableOpacity>

                    <TouchableOpacity onPress={this.onClikCrossIcon} style={styles.crossIconCta} activeOpacity={1}>
                        <IconButtonWrapper iconImage={icons.CROSSWHITE_ICON} iconHeight={dimens.size10} iconWidth={dimens.size10} />
                    </TouchableOpacity>
                </View>
            </View>
        )
    }

    render() {
        const { shouldRefresh, showScrollToTopView, dontShowGoToTop } = this.state
        const { getFlatListRef, uniqueKey, getScrolledPosition, renderScrollToTopView = false } = this.props
        return (
            <ScrollValueContext.Consumer>
                {(context) => (
                    <>
                        {showScrollToTopView && !dontShowGoToTop && renderScrollToTopView && this.renderScrollToTopView()}
                        <FlatListWrapper
                            {...this.props}
                            onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: context.scrollYValue } } }], {
                                useNativeDriver: true,
                                listener: ({ nativeEvent }) => {
                                    const yOffsetValue = get(nativeEvent, 'contentOffset.y', 0)
                                    log('Flatlist wrapper on event is called', yOffsetValue)
                                    this.scrolledValue = yOffsetValue
                                    // context.scrollYValue.setValue(this.scrolledValue)
                                    {
                                        renderScrollToTopView && this.showScrollToTopView()
                                    }
                                    if (getScrolledPosition) {
                                        getScrolledPosition(yOffsetValue)
                                    }
                                }
                            })}
                            refreshControl={
                                <RefreshControl
                                    refreshing={shouldRefresh}
                                    progressViewOffset={HEADER_HEIGHT}
                                    onRefresh={() => {
                                        this.onRefresh()
                                    }}
                                />
                            }
                            showCustomizedAnimatedFlatList={true}
                            contentContainerStyle={[styles.contentStyle, this.props.contentContainerStyle]}
                            scrollEventThrottle={16}
                            inputRef={(ref) => {
                                log('inputRefinputRefis called')
                                if (ref) {
                                    this.flatListRef = ref
                                    if (getFlatListRef) {
                                        getFlatListRef(ref)
                                    }
                                    context.addFlatListRef(this.flatListRef, uniqueKey)
                                }
                            }}
                            onMomentumScrollEnd={({ nativeEvent }) => {
                                const { contentOffset } = nativeEvent
                                if (contentOffset.y === 0) {
                                    log('inside onMomentumScrollEnd')
                                    context.flatListRef.forEach((item) => {
                                        if (item.key !== uniqueKey) {
                                            item.value.scrollToOffset({
                                                offset: 0,
                                                animated: false
                                            })
                                        }
                                    })
                                }
                            }}
                        />
                    </>
                )}
            </ScrollValueContext.Consumer>
        )
    }
}

所以我的代码是这样的。其中 flatlist 包装器只是 flatlist 的包装器。在平面列表中,当项目数小于 3 时,滚动到偏移量无法正常工作。如果项目数大于或等于 3,则工作正常。

标签: reactjsreact-nativereact-native-flatlist

解决方案


推荐阅读