使用refs定位动态呈现的ListView组件

时间:2016-11-02 11:02:09

标签: javascript reactjs react-native

教我自己通过制作聊天应用做出反应,现在当有人点击消息旁边的删除按钮(此代码中不可见,因为它不相关)时,它会从数据库中删除它,但我需要在此处进行更改在应用程序中。

为此,我为每个<Message/>组件设置了一个ref,并且我有一个与组件上的ref匹配的ref。但我需要定位具有该参考的实际组件节点。

是否有办法实现这一目标?如果没有,我还能做什么?

非常感谢

编辑:完整代码:

import React, { Component } from "react"
import { ListView, View, Text, StyleSheet, TextInput, TouchableHighlight } from "react-native"
import * as firebase from 'firebase';
import Icon from 'react-native-vector-icons/FontAwesome'
import { Hideo } from 'react-native-textinput-effects';

import Message from './Message'

export default class Chat extends Component {
  constructor() {
    super();
    this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    this.onSend = this.onSend.bind(this);
    this.state = {
      messages: this.ds.cloneWithRows([]),
      messageContent: '',
    };
  }
  componentWillMount() {
    // Get all of the messages from firebase database and push them into "nodes" array which then gets set in the "messages" state above in handleChat.
    const chatRef = firebase.database().ref().child('general');
    this.messages = [];
    chatRef.on('child_added', snap => {
      this.messages.push({user: snap.val().user.name,
          text: snap.val().text,
          messageId: snap.key
        })
        this.handleChat(this.messages);
      }
    ).bind(this);
    // this is what happens when someone removes a comment
    chatRef.on('child_removed', snap => {
      const messageId = snap.key; // <- This is the key in the database, for example: '-KVZ_zdbJ0HMNz6lEff'
      this.removeMessage(messageId);
    })
  }
  removeMessage(messageId){
      let messages = this.messages.filter(message => message.messageId !== messageId);
      this.handleChat(messages);
  }
  handleChat(messages) {
    this.setState({messages: this.ds.cloneWithRows(messages)})
  }
  onSend(messages) {
    const generalRef = firebase.database().ref().child('general');
    const user = firebase.auth().currentUser;
    generalRef.push(
      {
        _id: 1,
        text: this.state.messageContent,
        createdAt: new Date().getTime(),
        user: {
          _id: 2,
          name: user.displayName,
          avatar: 'http://mdepinet.org/wp-content/uploads/person-placeholder.jpg'
        }
      });
    this.setState({messageContent: ''})
  }
  removeMessage(messageId){
    let messages = this.messages.filter(message => message.messageId !== messageId);
    this.handleChat(messages);
  }
  render() {
    return (
      <View style={{flex: 1, alignItems: 'flex-end'}}>
        <ListView
        style={{ marginBottom: 60 }}
        enableEmptySections={true}
        dataSource={this.state.messages}
        renderRow={message => <Message name={message.user} text={message.text}/> }/>

      <Hideo
        style={{position: 'absolute', bottom: 0}}
        onChangeText={messageContent => this.setState({messageContent})} value={this.state.messageContent}        placeholder="Name"
        iconClass={Icon}
        iconName={'envelope'}
        iconColor={'white'}
        iconBackgroundColor={'#222'}
        inputStyle={{ color: '#464949' }}
      />
      <TouchableHighlight onPress={this.onSend} style={{position: 'absolute', alignItems: 'center',   bottom: 10, right: 10, borderRadius: 10, backgroundColor: '#d4af37'}}>
      <Text style={{color: 'whitesmoke', fontSize: 20, padding: 5}}>Send</Text>
      </TouchableHighlight>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  username: {
    fontFamily: 'AvenirNext-Bold'
  },
  comment: {
    fontFamily: 'AvenirNext-Regular'
  },
  bubble: {
    flex: 1,
    width: 250,
    backgroundColor: '#f5f5f5',
    margin: 15,
    padding: 10,
    borderRadius: 20
  }
})

1 个答案:

答案 0 :(得分:1)

对ListView行项使用refs不是一个好主意。正如Elmeister所说,我们只需要从数组中删除消息元素并更新ListView数据源,以便从ListView中删除消息。

以下是一个示例代码,可让您了解如何在应用中执行相同操作。

import React, {
  Component
} from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  ListView,
  TouchableHighlight,
} from 'react-native';


class StackOverflow extends Component {

  constructor(props) {
    super(props);
    const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    this.messages = this.getMessages();
    this.state = {
      dataSource: ds.cloneWithRows(this.messages)
    };
  }


  getMessages() {
    let arr = [];
    for (let i = 0; i < 100; i++) {
      arr.push({
        user: 'User ' + i,
        text: 'This is a sample user message ' + i,
        messageId: 'messageId' + i,
      });
    }
    return arr;
  }

  onDeletePress(messageId) {
    this.messages = this.messages.filter(message => message.messageId !== messageId);
    this.setState({
      dataSource: this.state.dataSource.cloneWithRows(this.messages)
    });
  }

  renderRow(rowData) {
    return (
      <View style={{padding:8}}>
        <Text>{rowData.user}</Text>
        <Text>{rowData.text}</Text>
        <TouchableHighlight
          style={{alignSelf :'flex-end',backgroundColor:'red'}}
          onPress={this.onDeletePress.bind(this,rowData.messageId)}>
          <Text>Delete</Text>
        </TouchableHighlight>
      </View>
    );
  }

  render() {
    return (
      <View style={{flex: 1, paddingTop: 22}}>
        <ListView
          dataSource={this.state.dataSource}
          renderRow={this.renderRow.bind(this)}
        />
      </View>
    );
  }
}

AppRegistry.registerComponent('StackOverflow', () => StackOverflow);

在上面的示例中,我们正在创建虚拟消息并将其保存在messages数组中并使用消息更新dataSource

调用onDeletePress后,我们将messageId传递给该方法,并将其传递到

下面

this.messages = this.messages.filter(message => message.messageId !== messageId);messages数组中删除邮件。然后我们更新将更新ListView的dataSource状态。

在您的代码中可能需要进行这些更改,

更改handleChat

handleChat(messages) {
    this.setState({messages: this.ds.cloneWithRows(messages)})
}

更新聊天组件,如下所示,

import React, {Component} from "react"
import {ListView, View, Text, StyleSheet, TextInput, TouchableHighlight} from "react-native"
import * as firebase from 'firebase';
import Icon from 'react-native-vector-icons/FontAwesome'
import {Hideo} from 'react-native-textinput-effects';

import Message from './Message'

export default class Chat extends Component {
  constructor() {
    super();
    this.chatRef = firebase.database().ref().child('general');
    this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    this.onSend = this.onSend.bind(this);
    this.state = {
      messages: this.ds.cloneWithRows([]),
      messageContent: '',
    };
  }

  componentWillMount() {
    // Get all of the messages from firebase database and push them into "nodes" array which then gets set in the "messages" state above in updateMessageList.
    this._messages = [];
    this.chatRef.on('child_added', snap => {
        this._messages.push({
          user: snap.val().user.name,
          text: snap.val().text,
          messageId: snap.key
        });
        this.updateMessageList(this._messages);
      }
    ).bind(this);
    // this is what happens when someone removes a comment
    this.chatRef.on('child_removed', snap => {
      const messageId = snap.key; // <- This is the key in the database, for example: '-KVZ_zdbJ0HMNz6lEff'
      this.removeMessage(messageId);
    }).bind(this);
  }

  removeMessage(messageId) {
    this._messages = this._messages.filter(message => message.messageId !== messageId);
    this.updateMessageList(this._messages);
  }

  updateMessageList(messages) {
    this.setState({messages: this.ds.cloneWithRows(messages)})
  }

  onSend(messages) {
    const user = firebase.auth().currentUser;
    this.chatRef.push(
      {
        _id: 1,
        text: this.state.messageContent,
        createdAt: new Date().getTime(),
        user: {
          _id: 2,
          name: user.displayName,
          avatar: 'http://mdepinet.org/wp-content/uploads/person-placeholder.jpg'
        }
      });
    this.setState({messageContent: ''})
  }

  render() {
    return (
      <View style={{flex: 1, alignItems: 'flex-end'}}>
        <ListView
          style={{ marginBottom: 60 }}
          enableEmptySections={true}
          dataSource={this.state.messages}
          renderRow={message => <Message name={message.user} text={message.text}/> }/>

        <Hideo
          style={{position: 'absolute', bottom: 0}}
          onChangeText={messageContent => this.setState({messageContent})} value={this.state.messageContent}
          placeholder="Name"
          iconClass={Icon}
          iconName={'envelope'}
          iconColor={'white'}
          iconBackgroundColor={'#222'}
          inputStyle={{ color: '#464949' }}
        />
        <TouchableHighlight onPress={this.onSend}
                            style={{position: 'absolute', alignItems: 'center',   bottom: 10, right: 10, borderRadius: 10, backgroundColor: '#d4af37'}}>
          <Text style={{color: 'whitesmoke', fontSize: 20, padding: 5}}>Send</Text>
        </TouchableHighlight>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  username: {
    fontFamily: 'AvenirNext-Bold'
  },
  comment: {
    fontFamily: 'AvenirNext-Regular'
  },
  bubble: {
    flex: 1,
    width: 250,
    backgroundColor: '#f5f5f5',
    margin: 15,
    padding: 10,
    borderRadius: 20
  }
});