类型'Promise <Item |的参数undefined> []'不可分配给'SetStateAction <Item []>'

时间:2020-07-30 14:52:13

标签: javascript firebase react-native async-await use-state

我是React的新手。我想创建一个使用ID列表从Firebase调用项目数据的函数。

我使用TypeScript,React Native和Hooks。

我尝试以下代码:

const FavoritesScreen = () => {
  const [favoritesData, setFavoritesData] = useState<Array<Item>>([])

  useEffect(() => {
    init()
  }, [])

  const init = async () => {
    const data = await getFavorites(sortByTimeStamp)
    setFavoritesData(data)
  }

  // ...

有人建议我在使用async / await时,将init函数的声明从useEffect挂钩中删除。到目前为止,一切正常。

getFavorites是异步声明的,因为它需要等待Firebase中的数据。

Visual Studio在setFavorites(data)中的单词data加上以下注释:

const data: Promise<Item | undefined>[]
Argument of type 'Promise<Item | undefined>[]' is not assignable to parameter of type 'SetStateAction<Item[]>'.
  Type 'Promise<Item | undefined>[]' is not assignable to type 'Item[]'.
    Type 'Promise<Item | undefined>' is missing the following properties from type 'Item': author, authorName, authorTitle, benefits, and 13 more.ts(2345)

对于我来说,这个问题是应允还是不确定的,也不清楚如何解决。

以下是完整的代码,如果有帮助的话:

import React, { useEffect, useState, useContext } from 'react'
import { Text, SafeAreaView, StyleSheet } from 'react-native'
import colors from '../constants/colors'
import { FirebaseContext, FavoritesContext } from 'src/firebase'
import { Item } from '../types/item'
import { Timestamp } from '@firebase/firestore-types'

type CompareFunction = (a: [string, Timestamp], b: [string, Timestamp]) => number

const FavoritesScreen = () => {
  const { firebase } = useContext(FirebaseContext)
  const { favorites } = useContext(FavoritesContext)
  const [favoritesData, setFavoritesData] = useState<Array<Item>>([])

  useEffect(() => {
    init()
  }, [])

  const init = async () => {
    // call the functions used in useEffect
    const data = await getFavorites(sortByTimeStamp)
    setFavoritesData(data)
  }

  // This function gets Firestore data for one id
  const getItemDataFromId = async (id: string) => {
    try {
      const data: Item = await firebase.db.collection('content').doc(id).get().data()
      return data
    } catch (error) {
      console.log(`Could not get Firebase data from Id: ${id}. Error message: ${error}`)
    }
  }

  // this function compare the timestamps for use in the Array.prototype.sort() method.
  function sortByTimeStamp(a: [string, Timestamp], b: [string, Timestamp]) {
    let am = 1
    let bm = 1
    try {
      const am = a[1].toMillis()
    } catch {
      return -1
    }
    try {
      const bm = b[1].toMillis()
    } catch {
      return 1
    }
    return am - bm
  }

  // Create an array of items data from the favorites object, sorted ny timestamp
  const getFavorites = async (compareFunction?: CompareFunction) => {
    // Convert the object to a [[key1, value1],[key2,value2],...) array
    const favoritesArray = Object.entries(favorites)
    // sort if needed
    compareFunction && favoritesArray.sort(compareFunction)
    // get the data from firebase
    const items = favoritesArray.map(async ([key, value]) => await getItemDataFromId(key))
    return items
  }

  return (
    <SafeAreaView style={styles.container}>
      <Text>Work in progress</Text>
    </SafeAreaView>
  )
}

//// Styles ////
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: colors.background,
    justifyContent: 'center',
    alignItems: 'center',
  },
})

export default FavoritesScreen

1 个答案:

答案 0 :(得分:1)

您可能遇到的第一个问题是在map函数中处理异步项目。这将返回解析为Item的诺言数组,而不是Item元素的数组。

一个简单的解决方法是将其包装在Promise.all中。

const items = favoritesArray.map(async ([key, value]) => await getItemDataFromId(key));
return Promise.all(items);

但是,在此之后,您可能会遇到返回类型为Array<Item | undefined>而不是Array<Item>的问题。有几种方法可以解决此问题。 items值可以强制转换为Item[],但最好过滤掉未定义的值。为此,我建议您查看at this question。例如:

items.filter((x): x is Item => x !== undefined);