React Native:组件状态中的重复项

React Native: Duplicate items in component state

我在 StackNavigator 中有两个屏幕,一个带有 FlatList,用于显示从 Firestore 检索的数据,另一个用于向数据库添加新数据。通过 navigation.goBack() 从堆栈中的第二个屏幕返回后,新项目应附加到列表中。相反,具有新项目的整个状态被附加到旧状态。数据库数据不包含重复项,刷新后,列表包含正确的元素。

我不知道我是否误解了组件生命周期或查询本身,因此非常感谢您的帮助。

export default class Main extends React.Component {

state = { chatData:[] }


componentDidMount = () => {
  // Make call to Cloud Firestore
  // for the current user, retrieve the chat document associated with each element in the chats id array
  let user = firebase.auth().currentUser;
  firestore().collection("users").doc(user.uid).onSnapshot((doc) => {
    doc.data().chats.map((element) => {
      firestore().collection("chats").doc(element).onSnapshot((doc) => {
        this.setState({chatData: [...this.state.chatData, 
          {id: element, subject: doc.data().subject, course: doc.data().course}]})
      })
    });
  })
}

添加课程并返回列表屏幕后的状态(重复元素)

设置状态时尝试使用prevState回调函数。像这样:

export default class Main extends React.Component {

state = { chatData:[] }


componentDidMount = () => {
  // Make call to Cloud Firestore
  // for the current user, retrieve the chat document associated with each element in the chats id array
  let user = firebase.auth().currentUser;
  firestore().collection("users").doc(user.uid).onSnapshot((doc) => {
    doc.data().chats.map((element) => {
      firestore().collection("chats").doc(element).onSnapshot((doc) => {
        // We use the parameter of the first argument of setState - prevState
        this.setState(prevState => ({chatData: [...prevState.chatData, 
          {id: element, subject: doc.data().subject, course: doc.data().course}]}))
      })
    });
  })
}

因为你想像累加器一样传播以前存在的状态以及你从 firestore 获得的新数据。如果您使用 this.state 执行此操作,那么您将再次添加它,因为它涉及已在状态中的数据,因此涉及 repeated/duplicated。如果有帮助,请告诉我。

尝试创建一个具有唯一值的新数组并将其分配给 chatData

componentDidMount = () => {
    let user = firebase.auth().currentUser;
    firestore().collection("users").doc(user.uid).onSnapshot((doc) => {
        doc.data().chats.map((element) => {
            firestore().collection("chats").doc(element).onSnapshot((doc) => {
                /**
                 * create a new array with unique values
                 */
                let newArray = [...this.state.chatData, { id: element, subject: doc.data().subject, course: doc.data().course }]
                let uniqueArray = [...new Set(newArray)]
                this.setState({
                    chatData: uniqueArray
                })
            })
        });
    })
}

希望对您有所帮助。有疑问欢迎留言。

这是我最终的解决方案。我正在使用 react-navigation addListener 在切换到第一个屏幕时调用 firestore API 并在导航到第二个屏幕时清除状态。我还从 onSnapshot() 切换到 get() 以进行 firestore 调用。

class Main extends React.Component {

  state = { currentUser: null, chatData:[]}


  componentDidMount = () => {
    console.log('A - component did mount')
    // definitely works
    this.willFocusSubscription = this.props.navigation.addListener(
      'willFocus',
      payload => {
        console.log('A- focus')
        this.readCourseData()
    }) 
    this.willBlurSubscription = this.props.navigation.addListener(
      'willBlur',
      payload => {
        console.log('A- blur')
        this.setState({chatData: []})
      })
  }

  componentWillUnmount() {
    console.log('A - component will unmount')
    this.willFocusSubscription.remove();
    this.willBlurSubscription.remove();
    // Remove the event listener
  }


  readCourseData = () => {
    // Make call to Cloud Firestore
    // for the current user, retrieve the chat document associated with each element in the chats array
    let user = firebase.auth().currentUser;
    firestore().collection("users").doc(user.uid).get().then((doc) => {
      doc.data().chats.map((element) => {
        firestore().collection("chats").doc(element).get().then((doc) => {
          let newArray = [...this.state.chatData, { id: element, subject: doc.data().subject, course: doc.data().course }]
          let uniqueArray = [...new Set(newArray)]
          this.setState({
              chatData: uniqueArray
          })
        })
      });
    })
  }