React Native FlatList scrollToBottom无效

时间:2018-01-31 09:21:05

标签: android react-native scroll react-native-flatlist

问候所有人,

我正在使用react-native FlatList,我的问题是我无法滚动到底部。这是我的FlatList代码:

<FlatList style = { styles.list }
  ref="flatList1"
  data = {
    this.state.conversation_arr
  }
  onContentSizeChange={(contentWidth, contentHeight) => {
    this.flat_list_height = contentHeight;
    // this.scrollNow();
  }}
  onLayout={this.scrollNow()}
  renderItem = { this._renderItem }
  keyExtractor = { item => item.message_id }
/>

scrollNow() {
  console.log("******",this.refs);
  if(this.refs.flatList1){
    this.refs.flatList1.scrollToEnd();
  }
}

当代码到达

this.refs.flatList1.scrollToEnd();

屏幕上出现错误

Type Error, cannot read property -1 of undefined.

这是截图。

cannot read property -1 of undefined 我基本上是在尝试为我的应用程序实现聊天功能,因此我需要在加载聊天时滚动到列表的底部。

感谢任何形式的帮助。 感谢

完整代码

import React, {
    Component
} from 'react';
import {
    StyleSheet,
    View,
    Text,
    TextInput,
    ActivityIndicator,
    TouchableOpacity,
    FlatList,
} from 'react-native';

export default class Conversation extends Component {

state = {
    conversation_arr: undefined,
    last_message_loaded: false,
    show_activty_indicator: true,
    message_text : undefined,
    message_text_length : 0
};


params = this.props.navigation.state;

last_evaluated_key = {
    message_id: undefined,
    timestamp: undefined
}
conversation = undefined;
thread_info = undefined;
flat_list_height = 0;


static navigationOptions = ({ navigation }) => ({
    title: navigation.state.params.header_title,
});

constructor(props) {
    super(props);

    this._renderItem = this._renderItem.bind(this);
    this._fetchConversation();
    this.scrollNow = this.scrollNow.bind(this);
    this.handleInputTextContent = this.handleInputTextContent.bind(this);
    this.sendMessage = this.sendMessage.bind(this);

    console.disableYellowBox = true;

    this.thread_info = this.params.params.thread_info;
}

// scrollNow() {
    // console.log("******",this.refs);
    // if(this.refs.flatList1){
    //     // debugger;
    //     this.refs.flatList1.scrollToEnd();
    // }

    // console.log("******", this.flatlist1);
    // if(this.flatlist1){
    //     this.flatlist1.scrollToEnd();
    // }
    // console.log("###", this.flat_list_height);
// }
scrollNow = () => {
    console.log("******", this.flatList1);
    if(this.state.conversation_arr && this.state.conversation_arr.length > 0 && this.flatList1){
        // debugger;

        // setTimeout(() => {
            // console.log("timer over now");
            this.flatList1.scrollToEnd();
        // }, 1000)

        // this.flatList1.scrollToEnd();
        console.log("Scrolling now at length", this.state.conversation_arr.length);

    }



    // !!this.refs["myFlatList"] && this.refs["myFlatList"].scrollToEnd()

}

componentDidUpdate(){
    console.log("componentDidUpdatecomponentDidUpdate")
}

componentWillUnmount(){
    console.log("componentWillUnmountv")
}

render() {
    return (
        <View style={ styles.container }>
            {   
                this.state.show_activty_indicator &&
                <View style={[styles.activity_indicator_container]}>
                    <ActivityIndicator
                        animating = { this.state.show_activty_indicator }
                        size="large"
                        color="#444444" 
                    />
                </View>
            }
            <FlatList style = { styles.list }
                // inverted
                // ref="myFlatList"
                // ref={(ref) => this.flatList1 = ref}
                ref={(ref) => { this.flatList1 = ref; }}
                data = {
                    this.state.conversation_arr
                }
                onContentSizeChange={(contentWidth, contentHeight) => {
                    this.flat_list_height = contentHeight;
                    // this.scrollNow();
                }}
                onLayout={this.scrollNow}
                renderItem = { this._renderItem }
                keyExtractor = { item => item.message_id }
            />

            <View style={ styles.bottom_box }>
                <Text style={ styles.message_length }> {this.state.message_text_length}/160 </Text>
                <View style={ styles.message_box }>
                    <View style={{flex: 3}} >
                        <TextInput
                            style={styles.input_text}
                            value={this.state.message_text}
                            onChangeText={this.handleInputTextContent}
                            placeholder='Type a message here...'
                            underlineColorAndroid='transparent'
                        />
                    </View>
                    <View style={{flex: 1}} >
                        <TouchableOpacity
                            style={styles.send_button} 
                            onPress={this.sendMessage} >
                                <Text style={[styles.label_send]}>SEND</Text>
                        </TouchableOpacity>
                    </View>
                </View>
            </View>
        </View>
    );

}

handleInputTextContent(text){

    if(text.length > 160){
        text = text.substring(0,159)
        this.setState({ message_text : text });
    } else {
        this.setState({ message_text : text });
        this.setState({ message_text_length : text.length });
    }
}

sendMessage(){
    console.log(this.state.message_text);
    if(this.state.message_text && this.state.message_text.length > 0) {
       console.log("Send message now");
    }
}

_renderItem(item, index){
    // console.log(item.item);
    item = item.item;

    let view = (
            <Text style = { [styles.msg_common] }> { item.message_content } </Text>
        )

    return   view ;
}

processPaymentMessages(dataArr) {
    return dataArr;
}

_fetchConversation() {

            new WebApi().fetchThreadConversation().then(result => {
                console.log("resutlresult", result);

                if (result && result.data && result.data.LastEvaluatedKey) {

                    this.last_evaluated_key.message_id = result.data.LastEvaluatedKey.message_id;
                    this.last_evaluated_key.timestamp = result.data.LastEvaluatedKey.timestamp;

                } else {
                    this.setState({
                        last_message_loaded: true
                    });
                    this.last_evaluated_key = null;
                }

                if (!this.conversation) {
                    this.conversation = [];
                }


                for (let i = 0; i < result.data.Items.length; i++) {

                        item = result.data.Items[i];
                        this.conversation.push(item);
                }

                // console.log(this.conversation);
                this.setState({ conversation_arr : this.conversation });
                this.setState({ show_activty_indicator : false });

                console.log(this.state.conversation_arr.length);


            }, error => {
                console.log("web api fetch data error", error);
            })
    }
}
    const styles = StyleSheet.create({
        activity_indicator_container: {
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#fff'
    },
container: {
    flex: 1,
    paddingTop: 8,
    backgroundColor:"#fff"
},

list : {
    paddingLeft: 8,
    paddingRight:8,
    marginBottom : 85,
},

gray_item: {
    padding: 10,
    height: 44,
    backgroundColor:"#fff"
},
white_item : {
    padding: 10,
    height: 44,
    backgroundColor:"#f5f5f5"
},

msg_common : {
    flex: 1,
    justifyContent: 'flex-end',
    alignItems: 'flex-end',
},
colored_item_common : {
    padding : 12,
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'

}, 
item_insider_common : {
    backgroundColor:"#fff",
    fontSize : 16,
    padding : 12,
    borderWidth : 1,
    borderRadius : 8,
    width : "90%",
    fontWeight : "bold",
    textAlign: 'center',

},
purple_item_insider : {
    color : "#7986cb",
    borderColor: "#7986cb",

}, 
green_item_insider : {
    color : "#66bb6a",
    borderColor: "#66bb6a",

},

bottom_box:{
    position: 'absolute',
    left: 0,
    right: 0,
    bottom: 0,
    flex: 1,
},
message_length: {
    height : 20,
    textAlign: 'right', alignSelf: 'stretch'
},
message_box: {
    // position: 'absolute',
    // left: 0,
    // right: 0,
    // bottom: 0,
    flex: 1, 
    flexDirection: "row"
},

input_text: {
    width: '100%',
    height: 60,
    color: '#000',
    backgroundColor:"#eee",
    padding: 16,
},
send_button: {
    alignItems: 'center',
    flex: 1, 
    height: 60,
    padding: 16,
    backgroundColor : '#f1592b'
},
label_send: {
    color: '#fff'
}
  })

3 个答案:

答案 0 :(得分:0)

你需要像这样回复onLayout:

onLayout={this.scrollNow}

更新的代码是:我试过了。它有效。

import React, { Component } from 'react';
import { FlatList, Text } from 'react-native';

export default class App extends Component {
  render() {
    return (
      <FlatList
        ref={(ref) => { this.flatList1 = ref; }}
        data={[0, 0, 0, 0, 0, 0, 0, 0]}
        onContentSizeChange={(contentWidth, contentHeight) => { this.flat_list_height = contentHeight; }}
        onLayout={this.scrollNow}
        renderItem={this.renderItem}
        keyExtractor={item => item.message_id}
      />
    );
  }
  renderItem =() => <Text style={{marginTop: 100}}>Just Some Text</Text>
  scrollNow =() => {
    if (this.flatList1) {
      this.flatList1.scrollToEnd();
    }
  }
}

答案 1 :(得分:0)

你试过传递一个空数组&#34; this.state.conversation_arr&#34;未定义或为null。检查this._renderItem也很有帮助,天气它试图访问任何参数的-1。

答案 2 :(得分:0)

您的代码中有两个部分错误。

  1. FlatList中的ref是一个函数,而不是字符串。
  2. 如果您不使用箭头功能,则FlatList无法访问scrollNow()函数中的“this”。
  3. 我用类似的代码测试过。下面的代码应该有效。

    import * as React from 'react';
    import { StyleSheet, View, Text, FlatList } from 'react-native';
    export default class Conversation extends React.Component {
      flatList1;
      _interval;
      state = { conversation_arr: [] };
    
      componentDidMount() {
        let buffer = [];
        for (let i = 1; i < 40; i++) {
          buffer.push({ message_id: i, message_content: `I'm the message ${i}` });
        }
    
        this._interval = setInterval(() => {
          this.setState(() => {
            return { conversation_arr: buffer };
          });
          this.flatList1.scrollToIndex({
            index: buffer.length - 1,
            viewPosition: 1,
            viewOffset: 0,
          });
        }, 1000);
      }
    
      componentWillUnmount() {
        clearInterval(this._interval);
      }
    
      render() {
        return (
          <View style={styles.container}>
            <FlatList
              ref={ref => {
                this.flatList1 = ref;
              }}
              data={this.state.conversation_arr}
              renderItem={this._renderItem}
              keyExtractor={item => item.message_id}
              getItemLayout={(data, index) => ({
                length: 40,
                offset: 40 * index,
                index,
              })}
            />
          </View>
        );
      }
    
      _renderItem = ({ item }) => {
        return (
          <View>
            <Text style={styles.msg_common}>{item.message_content}</Text>
          </View>
        );
      };
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
      },
      msg_common: {
        height: 40,
        fontSize: 18,
        paddingHorizontal: 10,
      },
    });
    

    PS:如果你有一个很长的列表,让我们说超过100.使用onLayout向下滚动可能需要一些时间进行初始渲染,这提供了非常糟糕的用户体验。更好的方法可能是

    1. 在呈现之前反转列表
    2. 提供排序按钮并在排序期间将onRefresh设置为true
    3. 提供滚动到底部按钮列表的页脚
    4. <强>更新 我注意到onContentSizeChange属性不在文档中。 https://facebook.github.io/react-native/docs/flatlist.html#scrolltoindex 您使用的是哪个版本的React-Native?

      <强> UPDATE2: TypeScript中的完整代码基于您的。我使用setInterval来模拟API调用的延迟。

      任何异步函数都应该在componentDidMount中,因为构造函数不会等待。

      getItemLayout用于帮助FlatList计算出高度。请注意,这是在数据到达之前设置的。