I'm learning to implement typescript in react and I'm struggling trying to implement a couple of async functions on context.
The error I'm getting is the following:
Argument of type '{ userData: null; favoriteCocktails: never[]; publishedRecipes: never[]; fetchUserData: (user: any) => PromiseConstructor; fetchFavoriteCocktails: (userData: UserData | null) => PromiseConstructor; fetchPublishedRecipes: (user: any) => PromiseConstructor; }' is not assignable to parameter of type 'ProfileContext'.
The types returned by 'fetchUserData(...)' are incompatible between these types.
Type 'PromiseConstructor' is missing the following properties from type 'Promise<void>': then, catch, finally, [Symbol.toStringTag]ts(2345)
This is my context:
import { createContext } from 'react'
import {UserData , Cocktail} from '../types'
interface ProfileContext {
userData: UserData | null,
favoriteCocktails: Array<Cocktail>,
publishedRecipes: Array<Cocktail>,
fetchUserData: (user: any) => Promise<void>,
fetchFavoriteCocktails: (userData: UserData | null) => Promise<void>,
fetchPublishedRecipes: (user: any) => Promise<void>,
}
const defaultState = {
userData: null,
favoriteCocktails: [],
publishedRecipes: [],
fetchUserData: (user: any) => Promise,
fetchFavoriteCocktails: (userData: UserData | null) => Promise,
fetchPublishedRecipes: (user: any) => Promise
}
const ProfileContext = createContext<ProfileContext>(defaultState)
export default ProfileContext
This is my provider:
import ProfileContext from './ProfileContext'
import { query, where, getDocs, collection } from 'firebase/firestore'
import { db } from '../services/firebase.config'
import { Cocktail, UserData } from '../types'
import { useState } from 'react'
export default function ProfileContextProvider ({ children }: { children: any }) {
const [userData, setUserData] = useState<UserData | null>(null)
const [favoriteCocktails, setFavoriteCocktails] = useState<Array<Cocktail>>([])
const [publishedRecipes, setPublishedRecipes] = useState<Array<Cocktail>>([])
const fetchUserData = async (user: any) => {
try {
const q = query(collection(db, 'mixrUsers'), where('email', '==', user.email))
const querySnapshot = await getDocs(q)
const result: any[] = []
querySnapshot.forEach(doc => result.push(doc.data()) )
setUserData(result[0])
} catch (err) {
console.error(err)
}
}
const fetchFavoriteCocktails = async (userData: UserData | null) => {
try {
const q = query(collection(db, 'mixrCocktails'), where('id', 'in', userData?.favoriteCocktails))
const querySnapshot = await getDocs(q)
const result: any[] = []
querySnapshot.forEach(doc => {result.push(doc.data())} )
setFavoriteCocktails(result)
} catch (err) {
console.error(err)
}
}
const fetchPublishedRecipes = async (user: any) => {
try {
console.log('fetchPublishedRecipes')
const q = query(collection(db, 'mixrCocktails'), where('publisherId', '==', user?.uid))
const querySnapshot = await getDocs(q)
const result: any[] = []
querySnapshot.forEach(doc => result.push(doc.data()) )
setPublishedRecipes(result)
} catch (err) {
console.error(err)
}
}
return (
<ProfileContext.Provider value={{ userData, favoriteCocktails, publishedRecipes, fetchUserData, fetchFavoriteCocktails, fetchPublishedRecipes }} >
{children}
</ProfileContext.Provider>
)
}
And I'm consuming it on different components in the following ways:
import { useEffect, useContext } from 'react'
import ProfileContext from '../context/ProfileContext'
import { RootTabScreenProps } from '../types'
import { useAuthState } from 'react-firebase-hooks/auth'
import { auth } from '../services/firebase.config'
import { StyleSheet, Text, View, SafeAreaView, ScrollView, Image, TouchableOpacity } from 'react-native'
import CocktailCard from '../components/home/cocktailCard/CocktailCard'
import GenericAvatar from '../assets/images/genericAvatar.jpg'
export default function ProfileScreen({ navigation }: RootTabScreenProps<'Profile'>) {
/* TODO - Profile context functions break */
const [user] = useAuthState(auth as any)
const {userData, favoriteCocktails, publishedRecipes, fetchUserData, fetchFavoriteCocktails, fetchPublishedRecipes} = useContext(ProfileContext)
useEffect(() => {
fetchUserData(user)
fetchPublishedRecipes(user)
}, [publishedRecipes])
useEffect(() => { if(userData) fetchFavoriteCocktails(userData) },[userData, favoriteCocktails])
/* ====================================== */
return (
<SafeAreaView style={styles.container}>
<ScrollView>
<View style={styles.profileHeader}>
<Image style={styles.profilePicture} source={user ? { uri: user?.photoURL } : GenericAvatar}/>
<Text style={styles.profileName}>{user?.displayName}</Text>
</View>
...
import { useContext, useEffect } from 'react'
import ProfileContext from '../context/ProfileContext'
import { RootTabScreenProps } from '../types'
import { useAuthState } from 'react-firebase-hooks/auth'
import { auth } from '../services/firebase.config'
import { Text, StyleSheet, View } from 'react-native'
import CocktailCard from '../components/home/cocktailCard/CocktailCard'
export default function PublishedRecipesScreen({ navigation }: RootTabScreenProps<'Published recipes'>) {
/* TODO - Profile context functions break */
const [user] = useAuthState(auth as any)
const { publishedRecipes, fetchPublishedRecipes } = useContext(ProfileContext)
useEffect(() => {fetchPublishedRecipes(user)}, [])
/* ====================================== */
return (
<View style={styles.container}>
<Text style={styles.title}>Published recipes</Text>
<View style={styles.cardsContainer}>
{publishedRecipes.map((cocktail, i) => <CocktailCard key={i} cocktail={cocktail} navigation={navigation} />)}
</View>
...
I understand there is a conflict between the types I'm declaring on the context file, and the actual types the functions are returning. But I don't understand how should I declare them.
This functions fetch data from a DB and update a state, so they don't return anything.
I've tried fetchUserData: (user: any) => void,
but I get Expression expected.ts(1109)
What's the right way to type this kind of functions?
Full code can be found here: https://github.com/coccagerman/mixr/
Thanks in advance!
CodePudding user response:
Here it is not a type :
const defaultState = {
fetchUserData: (user: any) => Promise,
fetchFavoriteCocktails: (userData: UserData | null) => Promise,
fetchPublishedRecipes: (user: any) => Promise
}
You must provide some default values, so some async functions that return nothing will do :
const defaultState = {
fetchUserData: async (user: any) => {},
fetchFavoriteCocktails: async (userData: UserData | null) => {},
fetchPublishedRecipes: async (user: any) => {}
}