React Native Flatlist отображает один и тот же элемент несколько раз при обновлении данных.

Компонент встроенного плоского списка react отображает один и тот же элемент столько раз, сколько список данных, когда данные обновляются. это для приложения чата. когда пользователь нажимает кнопку отправки, this.state.messages обновляется и вызывает повторную визуализацию плоского списка. однако при повторной визуализации все компоненты отображаются одинаково.

Chat.js

import React, { Component } from 'react';
import { View, FlatList } from 'react-native';
import { connect } from 'react-redux';
import { ChatBubble, Input } from './';

import { Header } from '../header';

class Chat extends Component {
  state={
    messages: [],
  };

  componentWillMount() {
    this.setState({
      messages: [
        {
          text: this.props.navigation.state.params.chat.lastMessage,
          direction: 'left',
          timeStamp: new Date().getTime(),
          id: '666',
          user: {
            displayName: this.props.navigation.state.params.chat.displayName,
          },
        }, {
          text: 'Thanks Nick!',
          timeStamp: new Date().getTime(),
          direction: 'right',
          id: '589',
          user: {
            displayName: 'You',
          },
        },
      ],
    });
  }

  onSend = (message) => {
    const messageData = {
      text: message,
      timeStamp: new Date().getTime(),
      direction: 'right',
      id: Math.random(1000).toString(),
      user: {
        displayName: 'You',
      },
    };
    if (message !== '') {
      this.setState({
        messages: this.state.messages.concat([messageData]),
      });
    }
    setTimeout(() => this.list.scrollToEnd(), 200);
  }

  renderMessages = ({ item }) => {
    return <ChatBubble message={item} />;
  }

  render() {
    return (
      <View style={{ flex: 1, backgroundColor: 'white' }}>
        <Header title={this.props.navigation.state.params.chat.displayName} />
        <FlatList
          keyboardShouldPersistTaps='always'
          data={this.state.messages}
          contentContainerStyle={{ backgroundColor: 'white', justifyContent: 'flex-end', flexGrow: 1 }}
          keyExtractor={message => message.id}
          renderItem={this.renderMessages}
          ref={(ref) => { this.list = ref; }}
        />
        <Input onPress={this.onSend} />
      </View>
    );
  }
}

const mapStateToProps = (state) => {
  let user;
  if (state.user.user) {
    user = state.user.user;
  }
  return { user };
};

export default connect(mapStateToProps)(Chat);

ChatBubble.js

import React, { Component } from 'react';
import { View, Clipboard, Text, TouchableWithoutFeedback } from 'react-native';
import moment from 'moment';

import { colors } from '../../config';

let message;

class ChatBubble extends Component {
  constructor(props) {
    super(props);
     message = this.props.message;
   }

  render() {
    return (
      <View style={styles[message.direction].container}>
        <View style={styles.topContainerStyle}>
          <Text style={styles[message.direction].infoStyle}>
            {message.user.displayName}
          </Text>
          <Text style={styles[message.direction].infoStyle}>
            {moment(message.timeStamp).format('LT')}
          </Text>
        </View>
        <Text style={styles[message.direction].message}>
          {message.text}
        </Text>
      </View>
    );
  }
}

const styles = {
  left: {
    container: {
      borderRadius: 20,
      borderBottomLeftRadius: 0,
      marginTop: 8,
      marginRight: 150,
      marginLeft: 10,
      paddingHorizontal: 10,
      paddingVertical: 5,
      alignSelf: 'flex-start',
      backgroundColor: colors.other.chatBubble,
    },
    message: {
      color: 'black',
      padding: 10,
      paddingTop: 5,
      fontFamily: 'avenir_roman',
      fontSize: 16,
    },
    infoStyle: {
      color: 'black',
      padding: 10,
      paddingBottom: 0,
      fontSize: 12,
      fontFamily: 'avenir_light',
    },
  },
  right: {
    container: {
      borderRadius: 20,
      borderBottomRightRadius: 0,
      marginTop: 8,
      marginRight: 10,
      marginLeft: 150,
      paddingHorizontal: 10,
      paddingVertical: 5,
      alignSelf: 'flex-end',
      backgroundColor: colors.secondary.blue,
    },
    message: {
      color: 'white',
      padding: 10,
      paddingTop: 5,
      fontFamily: 'avenir_roman',
      fontSize: 16,
    },
    infoStyle: {
      color: 'white',
      padding: 10,
      paddingBottom: 0,
      fontSize: 12,
      fontFamily: 'avenir_light',
    },
  },
  topContainerStyle: {
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
};

export { ChatBubble };

Какое поведение я должен получить

То, что я получаю сейчас


person Nithin Srinivasan    schedule 11.06.2018    source источник
comment
Если ваш массив, который вы указали в качестве реквизита data, состоит из повторяющихся данных, то плоский список отображает то же самое. Так что предоставьте соответствующие данные.   -  person Revansiddh    schedule 11.06.2018
comment
Я предоставляю соответствующие данные в плоский список. Это только рендеринг последнего индекса массива.   -  person Nithin Srinivasan    schedule 11.06.2018
comment
просто распечатайте свой this.state.messages. Вы можете видеть только свое последнее сообщение как часть массива.   -  person Revansiddh    schedule 11.06.2018
comment
ChatBubble - это пользовательский компонент? Не могли бы вы поделиться кодом ChatBubble, если это пользовательский компонент. Кстати, попробуйте с атрибутом key в ChatBubble один раз.   -  person Prasun    schedule 11.06.2018
comment
Я также добавил код пользовательского компонента ChatBubble выше. Поскольку в плоском списке есть экстрактор ключей, нет необходимости передавать ключ как опору в ChatBubble.   -  person Nithin Srinivasan    schedule 11.06.2018


Ответы (1)


Из кода, который вы упомянули в вопросе, похоже, проблема заключается в том, что состояние не удерживает предыдущий элемент массива при добавлении нового.

const messages = [...this.state.messages,messageData] //

рассматривая this.state.messages как массив, содержащий supposed data, и то же самое с messageData как отдельный элемент массива.

this.setState({ messages:messages  });
person Revansiddh    schedule 11.06.2018
comment
когда я console.log (this.state.messages), я получаю соответствующие данные. Я считаю, что предоставленное вами исправление эквивалентно объединению данных сообщения в состояние. Я думаю, что плоский список отображает только последний индекс this.state.messages - person Nithin Srinivasan; 11.06.2018
comment
Да, оба одинаковые. ни один плоский список не отображает весь предоставленный вами массив. - person Revansiddh; 11.06.2018
comment
есть ли другая причина, по которой может отображаться только последний индекс? - person Nithin Srinivasan; 11.06.2018
comment
Можете ли вы сделать проект на снэк.expo.io, попробовав, прокомментировав ref, keyextractor и прокрутив до конца событие. просто отобразить плоский список - person Revansiddh; 11.06.2018