You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在FlatList组件初始渲染时使用scrollToIndex?

目标

数据加载完成后,让<FlatList />滚动到指定索引,而非默认的索引0。

问题

初始化时,FlatList先滚动到索引1,随后又滚回顶部。
滚动异常效果

代码
import React, { useEffect, useRef, useState } from "react";
import { Button, Image, StyleSheet, Text, View, FlatList } from "react-native";

const dataStore = [
  {
    id: 1,
    message: "Hello World!"
  },
  {
    id: 2,
    message:
      "Donec tristique metus ac mauris dapibus, in pellentesque elit elementum. Vestibulum quam mi, sagittis eget congue vulputate, commodo in odio. Etiam euismod dui libero, dignissim porttitor diam tristique id. Sed ullamcorper sollicitudin massa, nec faucibus dolor congue quis. In id eros ut magna cursus semper. Etiam suscipit leo quis neque hendrerit, sit amet tempus neque cursus. Phasellus ac fringilla quam, sed dignissim justo. Integer a ligula quis justo dignissim varius eget at nunc. Duis nec mollis odio. Suspendisse tellus mauris, laoreet vitae massa eget, sollicitudin auctor nibh."
  },
  {
    id: 3,
    message:
      "Donec tristique metus ac mauris dapibus, in pellentesque elit elementum. Vestibulum quam mi, sagittis eget congue vulputate, commodo in odio. Etiam euismod dui libero, dignissim porttitor diam tristique id. Sed ullamcorper sollicitudin massa, nec faucibus dolor congue quis. In id eros ut magna cursus semper. Etiam suscipit leo quis neque hendrerit, sit amet tempus neque cursus. Phasellus ac fringilla quam, sed dignissim justo."
  },
  {
    id: 4,
    message:
      "Donec tristique metus ac mauris dapibus, in pellentesque elit elementum. Vestibulum quam mi, sagittis eget congue vulputate, commodo in odio. Etiam euismod dui libero, dignissim porttitor diam tristique id. Sed ullamcorper sollicitudin massa, nec faucibus dolor congue quis. In id eros ut magna cursus semper. Etiam suscipit leo quis neque hendrerit, sit amet tempus neque cursus."
  },
  {
    id: 5,
    message:
      "Donec tristique metus ac mauris dapibus, in pellentesque elit elementum. Vestibulum quam mi, sagittis eget congue vulputate, commodo in odio. Etiam euismod dui libero, dignissim porttitor diam tristique id. Sed ullamcorper sollicitudin massa, nec faucibus dolor congue quis."
  },
  {
    id: 6,
    message:
      "Donec tristique metus ac mauris dapibus, in pellentesque elit elementum. Vestibulum quam mi, sagittis eget congue vulputate, commodo in odio."
  }
];

// Api to get the data
const api = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(dataStore);
    }, 3000);
  });
};

function App() {
  const ref = useRef();
  const [data, setData] = useState([]);
  const [index, setIndex] = useState(0);

  // When the component is rendered, call the refreshData function
  useEffect(() => {
    refreshData();
  }, []);

  // Here we fetch the data
  // 1. When the data is retrived, we assigned it to 'data'
  // 2. Then we set an index of 1, where we want the flat list to scroll to
  const refreshData = async () => {
    const response = await api();
    setData(response);
    setIndex(1);
  };

  // Here we recognise that the index has changed
  // If the index is anything other than 0, we scroll to the given index
  useEffect(() => {
    if (index !== 0) {
      ref.current?.scrollToIndex({
        index: index,
        animated: true,
        viewPosition: 0.5
      });
    }
  }, [index]);

  // If data is empty, then we display loading component (see else)
  if (data.length !== 0) {
    return (
      <FlatList
        ref={ref}
        initialScrollIndex={index} // this will equal to 1

        // --*** IMPORTANT ***--
        // Without the below I will get the following error (see error in question)
        onScrollToIndexFailed={(info) => {
          const wait = new Promise((resolve) => setTimeout(resolve, 500));
          wait.then(() => {
            ref.current?.scrollToIndex({
              index: index,
              animated: true,
              viewPosition: 0.5
            });
          });
        }}
        data={data}
        renderItem={(info) => {
          return (
            <View
              style={{ margin: 20, padding: 10, backgroundColor: "#2F4F4F" }}
            >
              <Text style={{ color: "#FFF" }}>{info.item.message}</Text>
            </View>
          );
        }}
      />
    );
  } else {
    return (
      <View style={{ marginHorizontal: "auto", maxWidth: 500 }}>
        <Text style={{ fontSize: 30 }}>Loading...</Text>
      </View>
    );
  }
}

export default App;
代码注释提及的错误
scrollToIndex should be used in conjunction with getItemLayout or onScrollToIndexFailed, otherwise there is no way to know the location of offscreen indices or handle failures.

翻译:scrollToIndex应与getItemLayout或onScrollToIndexFailed配合使用,否则无法获知屏幕外索引的位置或处理失败情况。


解决方案

你的问题核心是双重滚动逻辑冲突:initialScrollIndex先触发滚动,随后useEffect监听index变化再次调用scrollToIndex,加上FlatList未提前获知列表项位置,导致最终滚动行为异常。以下两种方法可解决:

方法一:使用getItemLayout(推荐)

通过getItemLayout给FlatList提供每个列表项的位置信息,避免滚动计算错误,同时无需额外的useEffect滚动逻辑:

import React, { useEffect, useState } from "react";
import { Text, View, FlatList } from "react-native";

const dataStore = [/* 原数据不变 */];
const api = () => {/* 原api逻辑不变 */};

function App() {
  const [data, setData] = useState([]);
  const [targetIndex, setTargetIndex] = useState(0);

  useEffect(() => {
    refreshData();
  }, []);

  const refreshData = async () => {
    const response = await api();
    setData(response);
    setTargetIndex(1);
  };

  // 定义列表项布局:length为单个项的高度,需根据实际样式调整
  const getItemLayout = (data, index) => ({
    length: 200,
    offset: 200 * index,
    index,
  });

  if (data.length !== 0) {
    return (
      <FlatList
        initialScrollIndex={targetIndex}
        getItemLayout={getItemLayout} // 添加该属性
        data={data}
        renderItem={(info) => {
          return (
            <View
              style={{ margin: 20, padding: 10, backgroundColor: "#2F4F4F" }}
            >
              <Text style={{ color: "#FFF" }}>{info.item.message}</Text>
            </View>
          );
        }}
      />
    );
  } else {
    return (
      <View style={{ marginHorizontal: "auto", maxWidth: 500 }}>
        <Text style={{ fontSize: 30 }}>Loading...</Text>
      </View>
    );
  }
}

export default App;

注:如果列表项高度不固定,可通过onLayout获取实际高度后动态计算getItemLayout的值。

方法二:移除initialScrollIndex,仅依赖useEffect控制滚动

去掉initialScrollIndex避免双重触发,同时确保数据加载完成后再执行滚动:

// 修改FlatList部分,移除initialScrollIndex
<FlatList
  ref={ref}
  onScrollToIndexFailed={(info) => {
    const wait = new Promise((resolve) => setTimeout(resolve, 500));
    wait.then(() => {
      ref.current?.scrollToIndex({
        index: index,
        animated: true,
        viewPosition: 0.5
      });
    });
  }}
  data={data}
  renderItem={(info) => { /* 原renderItem逻辑不变 */ }}
/>

// 修改useEffect,增加data依赖,确保数据存在时才滚动
useEffect(() => {
  if (data.length > 0 && index !== 0) {
    ref.current?.scrollToIndex({
      index: index,
      animated: true,
      viewPosition: 0.5
    });
  }
}, [data, index]);

注:该方法适合高度不固定的场景,需保留onScrollToIndexFailed处理滚动失败的重试逻辑。


内容的提问来源于stack exchange,提问作者DeveloperLV

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 02:55:22