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!