【问题标题】:FlatList calls `onEndReached` when it's renderedFlatList 在渲染时调用 `onEndReached`
【发布时间】:2017-12-20 16:06:05
【问题描述】:

这是我的简单category list page 的 render() 函数。

最近我为我的FlatList 视图添加了分页,所以当用户滚动到底部时,onEndReached 在某个点被调用(onEndReachedThreshold 从底部开始的值长度),它将获取下一个@987654326 @ 并连接类别道具。

但我的问题是onEndReached 是在调用render() 时调用 换句话说,FlatList 的onEndReached 在到达底部之前就被触发了。

我为onEndReachedThreshold? 设置了错误的值吗?您发现有什么问题吗?

return (
  <View style={{ flex:1 }}>
    <FlatList
      data={this.props.categories}
      renderItem={this._renderItem}
      keyExtractor={this._keyExtractor}
      numColumns={2}
      style={{flex: 1, flexDirection: 'row'}}
      contentContainerStyle={{justifyContent: 'center'}}
      refreshControl={
        <RefreshControl
          refreshing = {this.state.refreshing}
          onRefresh = {()=>this._onRefresh()}
        />
      }
      // curent value for debug is 0.5
      onEndReachedThreshold={0.5} // Tried 0, 0.01, 0.1, 0.7, 50, 100, 700

      onEndReached = {({distanceFromEnd})=>{ // problem
        console.log(distanceFromEnd) // 607, 878 
        console.log('reached'); // once, and if I scroll about 14% of the screen, 
                             //it prints reached AGAIN. 
        this._onEndReachedThreshold()
      }}
    />
  </View>
)

更新我在这里获取this.props.categories 数据

  componentWillMount() {
    if(this.props.token) {
      this.props.loadCategoryAll(this.props.token);
    }
  }

【问题讨论】:

  • onEndReachedThreshold 取值范围是 0 和 1,其中 0 是滚动的顶部,1 是滚动的末尾。首先,我会尝试添加 0.7 并检查它是否按预期工作。让我知道它是否有帮助
  • 谢谢!但是 0.7 并没有解决问题。还是在第一时间触发onEndReached
  • 拥有这样的类变量怎么样。_mounted?
  • 如果是这种情况,您可以尝试添加一些条件渲染以仅在您的 categories 填充了值之后渲染您的 FlatList。像这样的东西:categories.length &gt; 0 ? &lt;FlatList... /&gt; : &lt; ActivityIndicator /&gt;
  • 您可以尝试在您的&lt;View&gt; 中添加此条件。像这样的东西:&lt;View&gt; {categories ? &lt;FlatList ... &gt; : &lt;Image ... /&gt; } &lt;/View&gt;。您可以在此处找到更多示例:reactjs.org/docs/conditional-rendering.html 让我知道它是否有效

标签: react-native


【解决方案1】:

尝试在FlatList 上实现onMomentumScrollBegin

constructor(props) {
    super(props);
    this.onEndReachedCalledDuringMomentum = true;
}

...

<FlatList
    ...
    onEndReached={this.onEndReached.bind(this)}
    onEndReachedThreshold={0.5}
    onMomentumScrollBegin={() => { this.onEndReachedCalledDuringMomentum = false; }}
/>

并修改您的onEndReached

onEndReached = ({ distanceFromEnd }) => {
    if(!this.onEndReachedCalledDuringMomentum){
        this.fetchData();
        this.onEndReachedCalledDuringMomentum = true;
    }
}

【讨论】:

  • 嗨!抱歉,我在其他问题上遇到了严重的技术问题,现在我又回到了这个问题 :) 我尝试了您的解决方案,但它只是停止了 onEndReached 行为。另外我不熟悉constructor(props) 语法,我们可以试试ES6 语法吗?我有点喜欢你的逻辑:)
  • 有趣。 onEndReached 只触发一次
  • 对不起,没有。它只呈现第一页
  • 嗨@llario我可以确认它调用onEndReached一次,即使你的解决方案,即this.setState({ onEndReachedCalledDuringMomentum: true })
  • 使用onMomentumScrollBegin 仅在您滚动滑行 (即拖拉&放手)...你慢慢拖到最后(不放手)。
【解决方案2】:

我已经解决了

<Flatlist
    ...
    onEndReached={({ distanceFromEnd }) => {
        if (distanceFromEnd < 0) return;
        ...
    }
    ...
/>

【讨论】:

  • 你能解释一下这个解决方案吗,它至少对我有用。屏幕向上或向下计数的距离?
【解决方案3】:

首先检查 FlatList 是否在 native-base 的 ScrollViewContent 内。然后把它拿出来 实际上你不需要使用ContentScrollView,因为FlatList 有ListFooterComponentListHeaderComponent

虽然不推荐,但是如果真的需要在ScrollView里面使用Flatlist,那么看看这个答案:https://stackoverflow.com/a/57603742/6170191

【讨论】:

  • 是的,我的 FlatList 在滚动视图中,删除后问题就消失了
  • @Nagibaba - 我在哪里可以找到有关 Flatlist 的其他建议和最佳实践以及一般的原生反应?我简要阅读了facebook.github.io/react-native/docs/performance,但没有发现任何关于在滚动视图中不使用 flatList 的内容。但话又说回来,我还没有通读完整的 react native 文档,因此指向特定页面的链接会有所帮助。
  • 亲爱的@sushrut619 react native 有很多瓶颈会严重影响性能。不要阅读整个文档。在 Google 上搜索react-native performance 会让您更清楚地了解许多流程,但您将来可能会发现很多问题。然后stackoverflow和github会帮你)
  • 经典视图怎么样?我需要在页面上放置一个绝对定位的元素以始终可用。 - 顺便说一句,即使我将FlatList 作为屏幕顶部的组件,onEndReached 仍然会在第一次渲染时调用,有时在第二次渲染时调用
【解决方案4】:

经过数小时尝试不同的方法后,我通过使用固定高度和 flex:1 的 View 包装 Flatlist 来实现它。 使用此设置,只有在滚动到底部附近之后,我才能调用一次 onEndReached。这是我的代码示例:

render() {
    const {height} = Dimensions.get('window');
    return (    
        <View style={{flex:1, height:height}}>
            <FlatList
                data={this.props.trips_uniques}
                refreshing={this.props.tripsLoading}
                onRefresh={()=> this.props.getTripsWatcher()}
                onEndReached={()=>this.props.getMoreTripsWatcher()}
                onEndReachedThreshold={0.5}
                renderItem={({item}) => (
                <View style={Style.card}> 
                    ...
                    ...
                </View>
                )}
                keyExtractor={item => item.trip_id}
            />
        </View>
    )
}

我的 onEndReached() 函数只是调用 API 并更新我的数据。它不会对到底部或阈值的距离进行任何计算

【讨论】:

  • 完美解决方案。
  • 如果您来到这里...请检查您是否设置了onEndReachedThreshold,文档听起来好像有一些合理的默认值,但实际上没有,以onEndReachedThreshold={0.5} 开头...
【解决方案5】:

大多数情况下,此错误是由于 onEndReachedThreashold 使用不正确造成的,这还取决于您正在渲染的项目数量(更多项目,更大滚动大小)。

尝试遵循这个逻辑:

如果 10 个项目覆盖了您的屏幕,并且您在每个滚动条上渲染 20 个项目,则将 onEndReachedThreashold 设置为 0.8。

如果 2 或 3 个项目覆盖您的屏幕,并且您在每个滚动条上渲染 10 个项目,则将 onEndReachedThreashold 设置为 0.5。

另外,使用 initialNumToRender = numItems。出于某种原因,使用此 FlatList 道具有助于减少多次 onEndReached 调用的机会。

只需使用 onEndReachedThreashold 值即可。


其他时候,由于嵌套滚动视图而产生此错误。不要将您的 FlatList 放在 ScrollView 中。相反,请使用 FlatList 页眉和页脚道具。


对于这两种解决方案,我建议将 FlatList 样式和 contentContainerStyle 设置为 { flexGrow: 1 }。

【讨论】:

    【解决方案6】:

    删除 FlatList 中的每个 可滚动视图

    【讨论】:

      【解决方案7】:

      如果您想显示 3 或 4 条记录,并且想在到达末尾时加载下一个数据。将 onEndReachedThreshold 设置为 0 或 0.1。

      【讨论】:

        【解决方案8】:

        也许您可以通过在执行异步调用之前增加您的 页面 来绕过这个 FlatList 错误,然后您将在每个 onEndReached fiers 上获取数据并且不会收到有关重复键的错误

        【讨论】:

          【解决方案9】:

          (截至 11 月 19 日)

          1. 将平面列表保留为单个视图内的唯一组件
          2. 从像
            {{flex: 1, height: Dimensions.get('window').height}}这样的维度设置单个视图的样式

          【讨论】:

            【解决方案10】:

            如果 FlatList 位于另一个 FlatList 或 ScrollView 上,则在渲染组件时立即调用 onEndReached 来解决该问题不会将 FlatList 与另一个包装在一起。

            【讨论】:

              【解决方案11】:

              我在&lt;Overlay&gt;(来自react-native-elements)中有一个&lt;FlatList&gt;(来自react-native)。我遇到了onEndReached在第一次渲染组件时被执行的问题在用户做任何事情之前。

              问题已通过使用 &lt;Modal&gt;(来自 react-native)而不是 &lt;Overlay&gt; 解决。

              【讨论】:

                【解决方案12】:

                如果你使用钩子,在这里你可以找到@Ilario 答案的钩子版本:

                const onEndReachedCalledDuringMomentum = useRef(true)
                
                onEndReachedHandler = ({ distanceFromEnd }) => {
                    if(!onEndReachedCalledDuringMomentum.current){
                        fetchData()
                        onEndReachedCalledDuringMomentum.current = true
                    }
                }
                
                <FlatList
                    ...
                    onEndReached={onEndReachedHandler}
                    onEndReachedThreshold={0.7}
                    onMomentumScrollBegin={() => { onEndReachedCalledDuringMomentum.current = false }}
                />
                
                

                【讨论】:

                  【解决方案13】:

                  这个简单的解决方案对我有用。请注意,“刷新”状态由 useEffect 挂钩中的异步 API 调用控制,以检索 FlatList 的数据。

                  const onEndReachedHandler = () => {
                      if (!refreshing) ...
                  }
                  
                  <FlatList
                      ...
                      data={mydata}
                      onEndReached={onEndReachedHandler}
                      onEndReachedThreshold={0.7}
                      refreshing={refreshing}
                  />
                  

                  【讨论】:

                    【解决方案14】:

                    我整天都在挣扎,但我遇到的问题是,我在 ScrollView 中使用 FlatList。因此,删除 Scrollview,然后独立使用 Flatlist。这将解决我的问题。

                    【讨论】:

                      【解决方案15】:

                      根据我的经验,您可以简单地在 FlatList 或 SectionList 中使用 onEndReachedThreshold 道具,然后向其传递一个非常小的数字,例如 0.001。

                      onEndReachedThreshold={0.001}
                      

                      根据docs for FlatList,onEndReachedThreshold 是列表项中从底部算起的长度单位。

                      距离结尾有多远(以列表的可见长度为单位) 列表的底部边缘必须从内容的末尾触发 onEndReached 回调。例如,值 0.5 将触发 onEndReached 当内容的结尾在可见的一半以内时 列表的长度。

                      因此,像 0.001 这样的非常小的值可以帮助您确保仅当内容的末尾在列表的可见长度的最末尾时才会调用 onEndReached。

                      希望这会有所帮助 :) 抱歉英语不好。

                      【讨论】:

                        【解决方案16】:

                        我已经通过使用 lodash 的 debounce 解决了这个问题。首先,我从“lodash.debounce”导入 debounce。然后我使用 debounce 以 500 毫秒间隔加载更多功能

                        <Flatlist onEndReached = {debounce(this._onLoadMore, 500)} />

                        【讨论】:

                        • 当平面列表定义了所有功能时,为什么我们要使用 lodash 来实现?为什么 react native 不能解决这些简单的问题。
                        • 虽然我是 debounce 的粉丝,但这个解决方案不起作用。该列表将在去抖动间隔后简单地调用回调。我们需要一个仅在用户到达列表末尾时运行的 onEndReach。
                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2018-04-05
                        • 1970-01-01
                        • 2019-06-24
                        • 1970-01-01
                        • 2020-07-09
                        相关资源
                        最近更新 更多