根据活动的轮播项目显示不同的数据

Show different data based on the active carousel item

我有一个水平的 ,这是我的 React-native 应用程序中的一个“旋转木马”,它在屏幕中央以及上一个和下一个项目的边缘显示一个项目。我想在滚动视图下方显示数据(课程)。 有人可以告诉我或指向资源,告诉我如何知道屏幕现在显示的是什么项目,然后显示基于该项目的数据吗?我是否需要计算滚动视图中的当前项目或者将其作为参数传递给某个函数?

我的目标:

父组件:

 return (
<View style={styles.screen}>
  <View style={styles.thumbnailScrollContainer}>
    <HorizontalContentScroll
      data={LESSONS_DATA}
    />
  </View>
  <View style={styles.dataScrollContainer}>
    <FlatList numColumns={2} data={lessonsByCategory} renderItem={renderLessonItem} />
  </View>
</View> );

这是我的水平滚动视图

const HorizontalContentScroll = ({ data}: HorizontalContentProps) => {
  const { width, height } = Dimensions.get('window');
  const scrollX = useRef(new Animated.Value(0)).current;
  const ITEM_SIZE = width * 0.8;

  const getInterval = (offset: any) => {
    // console.log('offset', offset);
  };

  const scrollableData = (data as Array<ContentCategory>).map(
    (item: ContentCategory, index: number) => {
      const inputRange = [
        (index - 1) * ITEM_SIZE,
        index * ITEM_SIZE,
        (index + 1) * ITEM_SIZE,
      ];
      const translateY = scrollX.interpolate({
        inputRange,
        outputRange: [40, 10, 40],
        // extrapolate: 'clamp',
      });

      return (
        <Card
          size="large"
          style={{
            ...styles.titleCard,
            transform: [{ translateY }],
            width: ITEM_SIZE,
          }}
          key={`${item.category}-${index}`}
        >
          <Text>{item.category}</Text>
        </Card>
      );
    }
  );

  return (
    <Animated.ScrollView
      contentContainerStyle={styles.contentContainer}
      horizontal
      onScroll={Animated.event(
        [{ nativeEvent: { contentOffset: { x: scrollX } } }],
        {
          useNativeDriver: true,
          listener: (event) => {
            getInterval(event);
          },
        }
      )}
      scrollEventThrottle={16}
      showsHorizontalScrollIndicator={false}
      bounces={false}
      pagingEnabled
      snapToAlignment="center"
      snapToInterval={330}
      decelerationRate={'fast'}
    >
      {scrollableData}
    </Animated.ScrollView>
  );
};

export default HorizontalContentScroll;

我想我必须在这个映射函数中做一些事情,比如将当前项目传递给我的父组件,但是怎么做呢?如果我尝试调用在父级中设置状态的函数,我会收到错误消息“警告:无法从不同组件的函数体内部更新组件。”

const scrollableData = (data as Array<ContentCategory>).map(
    (item: ContentCategory, index: number) => {
      const inputRange = [
        (index - 1) * ITEM_SIZE,
        index * ITEM_SIZE,
        (index + 1) * ITEM_SIZE,
      ];

      const translateY = scrollX.interpolate({
        inputRange,
        outputRange: [40, 10, 40],
      });

      // filterLessonsInTheParent(item)
      
      return (
        <Card
          size="large"
          style={{
            ...styles.titleCard,
            transform: [{ translateY }],
            width: ITEM_SIZE,
          }}
          key={`${item.category}-${index}`}
        >
          <Text>{item.category}</Text>
        </Card>
      );
    }

好的我解决了

我使用 Animated.Flatlist 而不是 Animated.Scrollview 这样我就可以得到 onViewableItemsChanged 道具,然后我不得不将我的组件重构为 class 组件这样 viewabilityConfig prop 就能正常工作了。

我在更新本地状态的 useCallback 函数中将当前可查看项传递给父级。然后我使用它和 React Pure Component 来避免重新渲染我的 HorizontalContentScroll 这会弄乱动画位置。 (我不知道这是否是最佳方式,但到目前为止它有效)。

// Parent
const handleViewableChange = useCallback((item: ContentCategory) => {
    setContentsToShow((prevItem) => item.contents);
  }, []);

  return (
    <View style={styles.screen}>
      <View style={styles.thumbnailScrollContainer}>
        <HorizontalContentScroll
          data={LESSONS_DATA}
          onViewableChange={handleViewableChange }
        />
      </View>
      <View>

          <FlatList
            numColumns={2}
            data={contentsToShow}
            renderItem={renderLessonItem}
          /> 
// HorizontalContentScroll
class HorizontalContentScroll extends PureComponent<HoriProps, any> {
  viewabilityConfig: { viewAreaCoveragePercentThreshold: number };
  scrollX: any;
  constructor(props: any) {
    super(props);

    this.handleViewableItemsChanged = this.handleViewableItemsChanged.bind(
      this
    );
    this.viewabilityConfig = { viewAreaCoveragePercentThreshold: 50 };
  }

  handleViewableItemsChanged = (info: any) => {
    const currItemInView = info.viewableItems[0].item;
    this.props.onViewableChange(currItemInView);
  };

  render() {
    const { data } = this.props;
    const { width, height } = Dimensions.get('window');
    const scrollX = new Animated.Value(0);
    const ITEM_SIZE = width * 0.8;

    return (
      <Animated.FlatList
        data={data}
        contentContainerStyle={styles.contentContainer}
        horizontal
        pagingEnabled
        onViewableItemsChanged={this.handleViewableItemsChanged}
        viewabilityConfig={this.viewabilityConfig}
        scrollEventThrottle={16}
        showsHorizontalScrollIndicator={false}
        bounces={false}
        snapToAlignment="center"
        snapToInterval={330}
        decelerationRate={'fast'}
        onScroll={Animated.event(
          [{ nativeEvent: { contentOffset: { x: scrollX } } }],
          {
            useNativeDriver: true,
          }
        )}
        renderItem={({
          item,
          index,
        }: {
          item: ContentCategory;
          index: number;
        }) => {
          const inputRange = [
            (index - 1) * ITEM_SIZE,
            index * ITEM_SIZE,
            (index + 1) * ITEM_SIZE,
          ];
          const translateY = scrollX.interpolate({
            inputRange,
            // [x, y, x]
            outputRange: [40, 10, 40],
            // extrapolate: 'clamp',
          });
          return (
            <Card
              size="large"
              style={{
                ...styles.titleCard,
                transform: [{ translateY }],
                width: ITEM_SIZE,
              }}
              key={`${item.category}-${index}`}
            >
              <Text style={styles.categoryText}>{item.category}</Text>
            </Card>
          );
        }}
      />
    );
  }
}

export default HorizontalContentScroll;