【问题标题】:FlatList onEndReached being called multiple times [duplicate]FlatList onEndReached 被多次调用[重复]
【发布时间】:2018-11-21 09:03:02
【问题描述】:

我正在制作一个反应原生项目,用户可以使用 Flickr API 搜索图像,其他一切工作正常,但我在实现分页时遇到了问题。我使用 FlatList 的 onEndReached 来检测用户何时滚动到列表的末尾,但问题是 onEndReached 被多次调用(包括第一次渲染期间的一次)。正如here 所说,我什至禁用了反弹,但它仍然被多次调用

 export default class BrowserHome extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoading: false,
      tagParam: "cat",
      pageNum: -1,
      data: [],
      photosObj: ""
    };
  }

  componentDidMount() {
    this.setState({
      isLoading: true
    });
    try {
      this.makeRequest();
    } catch {
      console.log("error has occurred");
    }
  }

  makeRequest = () => {
    const { tagParam, pageNum } = this.state;
    let url = `https://api.flickr.com/services/rest/? 
               method=flickr.photos.search
               &api_key=${apiKey}&format=json&tags=${tagParam}
               &per_page=30&page=${pageNum + 1}&nojsoncallback=1`;
    fetch(url, {
      method: "GET"
    })
      .then(response => response.json())
      .then(responseJSON => {
        this.setState({
          data: this.state.data.concat(responseJSON.photos.photo),
          isLoading: false,
          pageNum: responseJSON.photos.page
        });
      })
      .catch(error => {
        console.log(error);
        this.setState({ isLoading: false });
        throw error;
      });
  };

  render() {
    if (this.state.isLoading) {
      return <ActivityIndicator animating={true} size="large" />;
    }

    return (
      <View
        style={{
          flex: 1,
          height: 200,
          justifyContent: "flex-start",
          width: screenSize.width,
          backgroundColor: "black"
        }}
      >
        <Text>This is browserhome</Text>
        <FlatList
          style={{
            width: screenSize.width
          }}
          numColumns={3}
          data={this.state.data}
          keyExtractor={item => item.id}
          bounces={false}
          onEndReachedThreshold={1}
          onEndReached={({ distanceFromEnd }) => {
            this.loadMoreItem();
            alert("end reached call");
          }}
          renderItem={({ item, index }) => (
            <>
              <ImageTile imageURL={this.createImageURL(item)} />
            //  <Text style={{ color: "white" }}>
             //   {index}
             //   {console.log(index)}
             // </Text>
            </>
          )}
        />
      </View>
    );
  }

  createImageURL(item) {
    let server = item.server,
      id = item.id,
      secret = item.secret;
    let urlString = `https://farm${
      item.farm
    }.staticflickr.com/${server}/${id}_${secret}_s.jpg`;
    return urlString;
  }

  loadMoreItem() {
    this.makeRequest();
  }
}

【问题讨论】:

  • 1.将 onMomentumScrollBegin 属性添加到您的 FlatList 声明中。 { this.onEndReachedCalledDuringMomentum = false; }} /> 2. 修改您的onEndReached 回调以触发每个动量仅获取一次数据。 onEndReached = () => { if (!this.onEndReachedCalledDuringMomentum) { this.props.fetchData(); this.onEndReachedCalledDuringMomentum = true; } }; ```
  • Romit,下面的回答是否解决了您的问题?

标签: ios react-native reactive-programming react-native-flatlist


【解决方案1】:

这个解决方案对我有用。在FlatList组件中添加onMomentumScrollBegin并修改onEndReached

<FlatList
style = { ...}
data = {data}
initialNumToRender = {10}
onEndReachedThreshold = {0.1}
onMomentumScrollBegin = {() => {this.onEndReachedCalledDuringMomentum = false;}}
onEndReached = {() => {
    if (!this.onEndReachedCalledDuringMomentum) {
      this.retrieveMore();    // LOAD MORE DATA
      this.onEndReachedCalledDuringMomentum = true;
    }
  }
}
/>

【讨论】:

  • 感谢您的回答!仅使用 momentum 对我不起作用,但添加 initialNumToRender 对我有用
【解决方案2】:

您最好使用onEndReached 设置布尔值true,然后在此基础上使用onMomentumScrollEnd

onEndReached={() => this.callOnScrollEnd = true}
onMomentumScrollEnd={() => {
  this.callOnScrollEnd && this.props.onEndReached()
  this.callOnScrollEnd = false
}

【讨论】:

  • 救生员 10/10。
  • 捆绑了很多不同的方法,但这一个终于奏效了!非常感谢!
  • 如果你走到最后,然后再回去怎么办:/
  • 对不起@stevemoretz - 直到现在才看到你的评论。在这种情况下,应连续再次调用 onEndReached 和 onMomentumScrollEnd。
  • 谢谢!有道理是我的错,我考虑得不够好。
【解决方案3】:

这是我解决问题的方法:

这是我的初始状态:

state = {
  onEndReachedCalledDuringMomentum: true,
  lastLoadCount: 0,
}

这是我的平面列表

<FlatList
   keyboardShouldPersistTaps="always"
   style={...}
   data={this.state.searchResults}
   extraData={this.state}
   bounces={false}
   renderItem={({ item, index }) =>
         <SearchResultView
            uriSsource={item.image}
            itemIndex={index}
            name={item.name}
          />
   }
   showsVerticalScrollIndicator={false}
   keyExtractor={this._keyExtractor}
   numColumns={2}
   onEndReached={() => this._loadMoreData()}
   onEndReachedThreshold={0.01}
   ListFooterComponent={this._renderSearchResultsFooter}
   onMomentumScrollBegin={() => this._onMomentumScrollBegin()}
/>

这是我调用的函数:

// Key Extractor
    _keyExtractor = (item, index) => item.id;
// Check if list has started scrolling
    _onMomentumScrollBegin = () => this.setState({ onEndReachedCalledDuringMomentum: false });
// Load more data function
    _loadMoreData = () => {
            if (!this.state.onEndReachedCalledDuringMomentum) {
                this.setState({ onEndReachedCalledDuringMomentum: true }, () => {

                    setTimeout(() => {
                        if (this.state.lastLoadCount >= 20 && this.state.notFinalLoad) {
                            this.setState({

                                page: this.state.page + 1,
                            }, () => {
                                // Then we fetch more data;
                                this._callTheAPIToFetchMoreData();
                            });
                        };
                    }, 1500);
                });
            };
        };
// Show your spinner
    _renderSearchResultsFooter = () => {
            return (
                (this.state.onEndReachedCalledDuringMomentum && this.state.lastLoadCount >= 20 && this.state.notFinalLoad) ?
                    <View style={{ marginBottom: 30, marginTop: -50, alignItems: 'center' }}>
                        <ActivityIndicator size="large" color="#e83628" />
                    </View> : null
            )
        }

一旦我得到数据,在_callTheAPIToFetchMoreData() 内部,我会像这样更新状态:

this.setState({
  lastLoadCount: results.length,
  onEndReachedCalledDuringMomentum: results.length >= 20 ? true : false,
  notFinalLoad: results.length >= 20 ? true : false
}

编码愉快。

【讨论】:

    【解决方案4】:

    多次触发onEndReached的原因是你没有正确设置initialNumToRender

    onEndReached 在 VirtualizedList 中的 _maybeCallOnEndReached 中触发。

      _maybeCallOnEndReached() {
        const {
          data,
          getItemCount,
          onEndReached,
          onEndReachedThreshold,
        } = this.props;
        const {contentLength, visibleLength, offset} = this._scrollMetrics;
        const distanceFromEnd = contentLength - visibleLength - offset; 
        if (
          onEndReached &&
          this.state.last === getItemCount(data) - 1 &&
          distanceFromEnd < onEndReachedThreshold * visibleLength &&
          (this._hasDataChangedSinceEndReached ||
            this._scrollMetrics.contentLength !== this._sentEndForContentLength)
        ) {
        ...
    

    如果contentLength(一次渲染的内容长度)和visibleLength(通常是屏幕高度)接近,distanceFromEnd 可以非常小,因此distanceFromEnd &lt; onEndReachedThreshold * visibleLength 可以始终为true。通过设置initialNumToRender并控制contentLength的大小,可以避免不必要的onEndReached调用。

    这是一个例子。如果您在初始渲染时渲染 10 个 70 px 单元格的项目(这是 initialNumToRender 的默认道具),contentLength 变为 700。如果您使用的设备是 iPhoneX,则 visibleLength 是 724。 case distanceFromEnd 为 24,这将触发 onEndReached,除非您将 onEndReachedThreshold 设置为小于 0.03。

    【讨论】:

      【解决方案5】:

      您只需将onEndReachedThreshold 设置为可见长度的速率。 因此,您只需将其设置为小于 1 的数字。例如,零或 0.5 就可以了!!!!!!!

      如果这对你有用,请告诉我。

      【讨论】:

        【解决方案6】:

        onEndReached 被多次调用,使用 onMomentumScrollEnd 代替加载惰性数据。

        <FlatList
          data={data}
          keyExtractor={item => item.uid}
          onMomentumScrollEnd={retrieveData}
          onEndReachedThreshold={0}
        />
        

        【讨论】:

        • onMomentumScrollEnd 在滚动停止的地方触发retrieveData 函数,而不仅仅是列表的末尾。你确定你的解决方案???!!!
        【解决方案7】:

        您可以在接收数据时禁用滚动。在 FlatList 中设置 scrollEnabled false 同时加载数据。

        scrollEnabled={!this.props.loadingData}
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-04-05
          • 1970-01-01
          • 1970-01-01
          • 2020-07-09
          • 1970-01-01
          相关资源
          最近更新 更多