【问题标题】:FlatList ref scrollToIndex is not a functionFlatList ref scrollToIndex 不是函数
【发布时间】:2020-04-06 14:32:05
【问题描述】:

我在 react native 中面临一个似乎长期存在的问题。
我正在使用带有 RN 版本 0.59 的 Expo SDK35。由于代码库很大,我还没有更新到 Expo SDK36 / RN 0.60,但如果这可以解决我的问题,我可以更新。

我有一个 Animated.View 组件,它有一个 FlatList 子组件,我无法使用 FlatList 参考中应该可用的静态方法(尤其是 scrollToIndex())。请参阅下一个示例代码:

class Example extends React.Component{
    constructor(props){
        super(props);
        this.myRef = null;
    }

    componentDidUpdate = () => {
        /*
            somewhere in code outside this class, a re-render triggers
            and passes new props to this class.
            I do have props change detection, and some more other code,
            but I have removed it in order to minimize the code example here
        */

        // This call throws:
        // TypeError: undefined is not a function (near '...this._scrollRef.scrollTo...')
        this.myRef.scrollToIndex({
            animated: true,
            index: 1,
            viewOffset: 0,
            viewPosition: 0.5
        });

        // Other suggested solution from SO
        // This also throws:
        // TypeError: _this.myRef.getNode is not a function. (In '_this.myRef.getNode()', '_this.myRef.getNode' is undefined)
        this.myRef.getNode().scrollToIndex({
            animated: true,
            index: 1,
            viewOffset: 0,
            viewPosition: 0.5
        });
    }
    render = () => <Animated.View style={{ /* ... some animated props */ }}>
        <FlatList ref={(flatListRef) => { this.myRef = flatListRef; }}
            // more FlatList related props
        />
    </Animated.View>
}

我已尝试改用Animated.FlatList,但仍会引发与上述代码示例相同的错误。
我还尝试在收到的flatListRef 参数上使用 react native 的 findNodeHandle() 实用函数,但它返回 null

我发现过去在 Stack Overflow 上多次发布过相同的问题,大多数都没有答案,或者对我不起作用。这些帖子也有点旧(一年左右),这就是为什么我再次发布同一问题的原因。

有人设法找到解决此问题的解决方案/解决方法吗?

编辑:可能的解决方法

当我在玩代码时,我尝试使用 ScrollView 组件而不是 FlatList - scrollTo 方法有效!
更改仅在 FlatList - ScrollView 特定道具上(因此,对于 ScrollView,它将是子级而不是 data={[...]}renderItem={()=&gt;{ ... }} 等),以及 componentDidMount 中的 scrollToIndex 方法被 scrollTo 替换.
带有 ScrollView 的类的 render 方法现在看起来像这样:

    render = () => <Animated.View style={{ /* ... some animated props */ }}>
        <ScrollView ref={(flatListRef) => { this.myRef = flatListRef; }}>
            {/*
                this.renderItem is almost the same as the
                renderItem method used on the FlatList
             */}
             { this.state.dataArray.map(this.renderItem) }
        </ScrollView>
    </Animated.View>

请注意,ScrollView 没有 scrollToIndex() 方法,因此您必须手动跟踪子位置,并且可能需要实现自己的 scrollToIndex 方法。

我不会将此作为我问题的答案,因为根本问题仍然存在。但作为一种解决方法,也许你可以接受它并收工......

【问题讨论】:

    标签: react-native animation reference undefined react-native-flatlist


    【解决方案1】:

    TL;DR;

    
    this.myRef = React.createRef();
    this.myRef.current.doSomething(); // note the use of 'current'
    

    长版:

    虽然我尝试的想法是正确的,但我原来帖子中的错误似乎很愚蠢。在我的辩护中,文档并不清楚(可能......)。总之……

    React.createRef 返回一个带有几个字段的对象,所有这些字段对开发人员都无用(由 React 在后面使用)- 除了一个:current。 此道具保存对 ref 附加到的基础组件的当前引用。主 ref 对象不能用于我在上面的原始问题中想要达到的目的。

    相反,这是我应该正确使用 ref 的方式:

    this.myRef.current.scrollToIndex(...)
    

    等一下,不要崩溃

    如果组件尚未安装、稍后已卸载或由于某种原因无法附加引用,则主 myRef 对象和 current 字段将是 null。您可能知道(或稍后发现),null.something 会抛出错误。所以,为了避免它:

    if ((this.myRef !== null) && (this.myRef.current !== null)){
        this.myRef.current.scrollToIndex(...);
    }
    

    额外保险

    如果您尝试在 ref 上的字段上调用未定义的值作为函数,您的代码将崩溃。如果您错误地在多个组件上重复使用相同的 ref,或者您附加它的组件没有该方法(即View 没有scrollTo 方法),就会发生这种情况。要解决此问题,您有两种解决方案:

    // I find this to be the most elegant solution
    if ((this.myRef !== null) && (this.myRef.current !== null)) {
        if (typeof this.myRef.current.scrollToIndex === "function") {
            this.myRef.current.scrollToIndex(...);
        }
    }
    

    if ((this.myRef !== null) && (this.myRef.current !== null)) {
        if (typeof this.myRef.current.scrollToIndex === "function") {
            try {
                this.myRef.current.scrollToIndex(...);
            } catch (error) {
                console.warn("Something went wrong", error);
            }
        }
    }
    

    我希望这对学习在 React 中使用 refs 的其他人有用。干杯:)

    【讨论】:

    • 谢谢伙计。我正面临着这个,失去了三个宝贵的小时。您的解决方案修复了它。再次感谢...@克里斯蒂安
    【解决方案2】:

    使用 Animated.ScrollView

    • 为您的 FlatList 创建一个引用(旧方法仅适用):

    &lt;ScrollView ref={ (ref) =&gt; (this.MyRef=ref) } /&gt;

    • 使用this.myRef.getNode().scrollToIndex访问scrollToIndex

    Animated.FlatList 目前无法正常工作...

    使用平面列表

    • 通过以下方式为您的 FlatList 创建一个引用:

    &lt;FlatList ref={ this.flatListRef } /&gt;

      constructor(props) {
        super(props);
    
        this.flatListRef = React.createRef();
      }
    
    • 使用this.flatListRef.current.scrollToIndex访问scrollToIndex

    还要确保将代码包装在 if 语句中,例如:

     if (this.myRef.getNode()) { this.flatListRef.getNode().scrollToIndex(); }
    

    【讨论】:

    • 不幸的是,它没有用。我尝试了FlatListAnimated.FlatList 两种方式,它总是抛出this.myRef.scrollToIndex is not a functionthis.myRef.getNode is not a function。 :(
    • 请按照Animated.FlatList 中的步骤...并尝试在componentDidMount 方法中使用console.log(this.flatListRef.getNode())
    • 我确实按照您描述的步骤进行了操作,并且我还尝试了 Animated.FlatList 和 FlatList、getNode() 和直接调用参考的混合。使用 getNode() 它会在 if 语句中抛出。没有 getNode() 它会抛出引用,就像我最初描述的那样。好消息是 - 我可以改用ScrollView。在 ScrollView 上放置一个 ref 并尝试使用与 FlatList 相同的代码(除了 renderItem、data 和其他与 FlatList 相关的道具),它可以正常工作。我不知道为什么,对我来说没有意义,但是嘿......这可能是一个解决方案......
    • 我之前尝试过Animated.FlatList,但它没有用......错误......问题是 -> 用 ScrollView 替换你的 FlatList 会导致性能下降(FlatList 更多优化...)
    • 我知道性能下降,但是,我画的孩子很少(确切地说是 4 个),而且我只在需要时以编程方式滚动它们,希望不会对性能产生太大影响。但是,仍然感谢您尝试帮助我:)
    【解决方案3】:

    不知道这是否对您有帮助...它滚动到列表中的特定项目:

    /*Example to Scroll to a specific position in scrollview*/
    import React, { Component } from 'react';
    //import react in our project
    
    import {
      View,
      ScrollView,
      StyleSheet,
      Text,
      TouchableOpacity,
      Image,
      TextInput,
    } from 'react-native';
    //import all the components we needed
    
    export default class App extends Component {
      constructor() {
        super();
        //Array of Item to add in Scrollview
        this.items = [
          'zero',
          'one',
          'two',
          'three',
          'four',
          'five',
          'six',
          'seven',
          'eight',
          'nine',
          'ten ',
          'eleven',
          'twelve',
          'thirteen',
          'fourteen',
          'fifteen',
          'sixteen',
          'seventeen',
          'eighteen',
          'nineteen',
          'twenty ',
          'twenty-one',
          'twenty-two',
          'twenty-three',
          'twenty-four',
          'twenty-five',
          'twenty-six',
          'twenty-seven',
          'twenty-eight',
          'twenty-nine',
          'thirty',
          'thirty-one',
          'thirty-two',
          'thirty-three',
          'thirty-four',
          'thirty-five',
          'thirty-six',
          'thirty-seven',
          'thirty-eight',
          'thirty-nine',
          'forty',
        ];
        //Blank array to store the location of each item
        this.arr = [];
        this.state = { dynamicIndex: 0 };
      }
      downButtonHandler = () => {
        if (this.arr.length >= this.state.dynamicIndex) {
          // To Scroll to the index 5 element
          this.scrollview_ref.scrollTo({
            x: 0,
            y: this.arr[this.state.dynamicIndex],
            animated: true,
          });
        } else {
          alert('Out of Max Index');
        }
      };
      render() {
        return (
          <View style={styles.container}>
            <View
              style={{
                flexDirection: 'row',
                backgroundColor: '#1e73be',
                padding: 5,
              }}>
              <TextInput
                value={String(this.state.dynamicIndex)}
                numericvalue
                keyboardType={'numeric'}
                onChangeText={dynamicIndex => this.setState({ dynamicIndex })}
                placeholder={'Enter the index to scroll'}
                style={{ flex: 1, backgroundColor: 'white', padding: 10 }}
              />
              <TouchableOpacity
                activeOpacity={0.5}
                onPress={this.downButtonHandler}
                style={{ padding: 15, backgroundColor: '#f4801e' }}>
                <Text style={{ color: '#fff' }}>Go to Index</Text>
              </TouchableOpacity>
            </View>
            <ScrollView
              ref={ref => {
                this.scrollview_ref = ref;
              }}>
              {/*Loop of JS which is like foreach loop*/}
              {this.items.map((item, key) => (
                //key is the index of the array
                //item is the single item of the array
                <View
                  key={key}
                  style={styles.item}
                  onLayout={event => {
                    const layout = event.nativeEvent.layout;
                    this.arr[key] = layout.y;
                    console.log('height:', layout.height);
                    console.log('width:', layout.width);
                    console.log('x:', layout.x);
                    console.log('y:', layout.y);
                  }}>
                  <Text style={styles.text}>
                    {key}. {item}
                  </Text>
                  <View style={styles.separator} />
                </View>
              ))}
            </ScrollView>
          </View>
        );
      }
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        paddingTop: 30,
      },
      separator: {
        height: 1,
        backgroundColor: '#707080',
        width: '100%',
      },
    
      text: {
        fontSize: 16,
        color: '#606070',
        padding: 10,
      },
    });
    

    如果我完全错了,请告诉我...

    【讨论】:

    • 谢谢,不过,问题不在于如何 滚动到某个索引;问题是从这些组件的 ref 属性返回的 ScrollView 或 FlatList 引用是 无效引用,或者根本不是引用(返回 null)。因此,这使得调用任何静态 scrollTo*...* 方法成为不可能,因为没有东西可以进行调用。
    【解决方案4】:

    因为 ScrollView 没有 scrollToOffset 函数,它只有 scrollTo 函数。

    所以让 scrollTo 和 ScrollView 一起使用或 scrollToOffset 和 FlatList 一起使用,它可以正常工作。

    【讨论】:

    • 这不是问题,我最初使用的是具有 scrollToIndex 方法的 FlatList。我现在已经发布了我的问题的正确解决方案,请查看我上面的答案:)
    【解决方案5】:

    如果您正在使用“KeyboardAwareFlatList”,则效果很好: https://github.com/APSL/react-native-keyboard-aware-scroll-view/issues/372

    简而言之,使用 useRef 并使用KeyboardAwareFlatListinnerRef 属性而不是ref 属性。

    【讨论】:

      猜你喜欢
      • 2019-02-17
      • 1970-01-01
      • 1970-01-01
      • 2021-07-04
      • 2021-04-29
      • 1970-01-01
      • 1970-01-01
      • 2022-11-14
      • 2021-12-30
      相关资源
      最近更新 更多