0

my package.json

 "@expo/vector-icons": "^13.0.0",
"@react-native-firebase/app": "^17.1.0",
"@react-native-firebase/auth": "^17.1.0",
"@react-native-firebase/firestore": "^17.1.0",
"@react-navigation/bottom-tabs": "^6.5.4",
"@react-navigation/core": "^6.4.6",
"@react-navigation/native": "^6.1.3",
"@react-navigation/native-stack": "^6.9.9",
"@reduxjs/toolkit": "^1.9.2",
"expo": "~47.0.13",
"expo-splash-screen": "~0.17.5",
"expo-status-bar": "~1.4.2",
"react": "18.1.0",
"react-native": "0.70.5",
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "~3.18.0",
"react-redux": "^8.0.5",
"redux": "^4.2.1"

i create a slice store a createSynchThunk for my call api with firebase

export const fetchProductCatList = createAsyncThunk(
    "productCat/fetchProductCatList",
    (param) => {
        apiGetProductCat()
            .then(e => {
                return e
            }).catch(err => {
                return err
            })
    }
);

My extra reducer
extraReducers: (builder) => {
        // Add reducers for additional action types here, and handle loading state as needed
        builder.addCase(fetchProductCategoriesList.fulfilled, (state, action) => {
            console.log('extraReducers fetchProductCategoriesList action.payload', action)
        })
    },

my function callApi

export function apiGetProductCat() {
    return new Promise((resolve, reject) => {
        firestore()
            .collection('product-categories')
            .get()
            .then(querySnapshot => {
                let map = {}
                querySnapshot.forEach(documentSnapshot => {
                    const data = documentSnapshot.data()
                    console.log('data', data)
                    map[documentSnapshot.id] = {}
                    map[documentSnapshot.id] = {
                        id: documentSnapshot.id,
                        ...data
                    }
                });
                resolve(map)
            })
            .catch(err => {
                reject(true)
            });
    })
}

I start my action in my view react-native

 useMemo(async () => {
        await props.dispatch(fetchProductCatList())
        setLoaderScreen(false)
    },[])

My Database firebase enter image description here

My error function apiGetProductC in catch :

[TypeError: undefined is not a function (near '...this._firestore.native.collectionGet...')]

I can connect user with login and password. I can use Firebase.auth() to retrieve my email user. But i can make a get for my collection... I don't understand.

Thanks for help!

update 02/14/2023 -> I updated Expo in package.json

 "expo": "~47.0.12"

library in build.gradle

classpath('com.android.tools.build:gradle:7.3.1')
classpath('com.google.gms:google-services:4.3.15')

but it not resolved my problem

Seb
  • 404
  • 2
  • 4
  • 14
  • What is the value of `COLLECTION`? where is it defined? Also, please review [this question thread](https://stackoverflow.com/q/23803743/3068190) about the Promise Constructor Antipattern and correct your code accordingly. – samthecodingman Feb 13 '23 at 02:01
  • it's my collection in my firebase , it's a constant – Seb Feb 13 '23 at 02:03
  • I believe my problem concern the code but not. I have a librairy and follow the example. And i can loggin my user in the database. So I don't understand. I update my post – Seb Feb 14 '23 at 01:26

1 Answers1

1

First, let's take a look at this code:

export function apiGetProductCat() {
    return new Promise((resolve, reject) => {
        firestore()
            .collection('product-categories')
            .get()
            .then(querySnapshot => {
                let map = {}
                querySnapshot.forEach(documentSnapshot => {
                    const data = documentSnapshot.data()
                    console.log('data', data)
                    map[documentSnapshot.id] = {}
                    map[documentSnapshot.id] = {
                        id: documentSnapshot.id,
                        ...data
                    }
                });
                resolve(map)
            })
            .catch(err => {
                reject(true)
            });
    })
}

Effectively, your code does this:

export function doTask() {
    return new Promise((resolve, reject) => {
        startTask()
            .then(result => {
                resolve(result)
            })
            .catch(err => {
                reject(err)
            });
    })
}

This is known as a Promise Constructor Antipattern, which leads to a number of problems (discussed on the linked thread). The exact same code can be written as:

export function doTask() {
    return startTask();
}
// or even:
// export const doTask = startTask;

These lines override each other:

map[documentSnapshot.id] = {} // delete this line
map[documentSnapshot.id] = {
    id: documentSnapshot.id,
    ...data
}

These lines suppress errors and will make debugging more difficult - delete them.

.catch(err => {
    reject(true)
});

Applying these changes to your apiGetProductCat() function gives:

export function apiGetProductCat() {
    return firestore()
        .collection('product-categories')
        .get()
        .then(querySnapshot => {
            const map = {};
            querySnapshot.forEach(documentSnapshot => {
                const data = documentSnapshot.data()
                console.log('data', data)
                map[documentSnapshot.id] = {
                    id: documentSnapshot.id,
                    ...data
                }
            });
            return map;
        });
}

Next we can move onto:

export const fetchProductCatList = createAsyncThunk(
    "productCat/fetchProductCatList",
    (param) => {
        apiGetProductCat()
            .then(e => {
                return e
            }).catch(err => {
                return err
            })
    }
);

In the above block, the apiGetProductCat() is "floating" - its result is never returned anywhere, nor its errors.

These lines are redundant and should be removed:

.then(e => {
    return e // returning the same value
})

These lines suppress errors (which you should only do when you intend to):

.catch(err => {
    return err // silently suppresses the error
})

Pruning them allows fetchProductCatList to become:

export const fetchProductCatList = createAsyncThunk(
    "productCat/fetchProductCatList",
    () => apiGetProductCat() // or even just apiGetProductCat directly
);

Lastly, you switch between fetchProductCategoriesList and fetchProductCatList - make sure your code is consistent. Correcting your extraReducers property gives:

extraReducers: (builder) => {
    // Add reducers for additional action types here, and handle loading state as needed
    builder
        .addCase(fetchProductCatList.fulfilled, (state, action) => {
            // TODO: Handle success
            console.log('extraReducers fetchProductCatList action.payload', action.payload)
        })
        .addCase(fetchProductCatList.rejected, (state, action) => {
            // TODO: Handle error
            console.error('extraReducers fetchProductCatList action.error', action.error)
        })
        // .addCase(fetchProductCatList.pending, (state, action) => { /* ... */ )
}
samthecodingman
  • 23,122
  • 4
  • 30
  • 54
  • oh wonderfull, thanks for your response. I didn't unknow the 'Promise Constructor Antipattern'. Thanks for your best pratices. – Seb Feb 16 '23 at 08:28