Reputation: 346
I am new to React Native, and this is my first project using React Native that build from scratch. I have a warning issue and the feature i want is not working.
Here are some explanation of what i want : I have an array of object that i named it as carts, inside the carts it looks like :
let carts = [
{
merchantId: 1,
items: [
{
productId: 1,
productQty: 1,
},
{
productId: 2,
productQty: 5,
},
],
},
{
merchantId: 2,
items: [
{
productId: 3,
productQty: 2,
},
{
productId: 4,
productQty: 4,
},
],
},
];
And this is my Screen :
import React, { useState, useEffect } from 'react';
import {
StyleSheet,
Text,
View,
Image,
ScrollView,
TouchableOpacity,
} from 'react-native';
import { Button } from 'react-native-paper';
import { theme } from '../../../infrastructure/theme';
import SpacerComponent from '../../../components/SpacerComponent';
import { FontAwesome5 } from '@expo/vector-icons';
import MerchantCardComponent from '../components/MerchantCardComponent';
import { tranformCartArray } from '../../../services/carts/CartTransform';
const CartScreen = ({ navigation }) => {
const [carts, setCarts] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const getCarts = async () => {
setIsLoading(true);
const response = await tranformCartArray();
setCarts(response);
setIsLoading(false);
};
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
getCarts();
});
return unsubscribe;
}, [navigation]);
console.log(carts);
if (carts.length === 0) {
return (
<View style={styles.emptyContainter}>
<Image
style={styles.imageCart}
source={require('../../../assets/images/empty_cart.png')}
/>
<Text style={styles.emptyCartText}>
Keranjangnya masih kosong nih !
</Text>
<SpacerComponent size="xlarge" />
<Button
style={styles.buttonExplore}
mode="contained"
size={20}
onPress={() => navigation.navigate('Merchants')}
>
<FontAwesome5
name="shopping-cart"
size={20}
color={theme.colors.text.inverse}
/>
<Text>{' '}</Text>
<Text style={styles.buttonText}>Eksplor</Text>
</Button>
</View>
);
}
if (isLoading) {
return (
<View style={styles.emptyContainter}>
<ActivityIndicator
color={theme.colors.ui.primary}
size={theme.sizes[3]}
/>
</View>
);
}
return (
<ScrollView style={styles.container}>
{carts.map((merchantAndItems) => {
return (
<TouchableOpacity onPress={() => navigation.navigate('Checkout')}>
<MerchantCardComponent
merchantAndItems={merchantAndItems}
key={merchantAndItems.merchantId}
/>
</TouchableOpacity>
);
})}
</ScrollView>
);
};
export default CartScreen;
const styles = StyleSheet.create({
emptyContainter: {
justifyContent: 'center',
alignItems: 'center',
flex: 1,
backgroundColor: theme.colors.bg.primary,
},
imageCart: {
maxWidth: '90%',
maxHeight: '45%',
aspectRatio: 1,
},
emptyCartText: {
fontSize: 20,
fontFamily: theme.fonts.regular,
},
buttonExplore: {
backgroundColor: theme.colors.ui.primary,
},
buttonText: {
fontSize: 20,
},
container: {
flex: 1,
marginBottom: 10,
},
});
I want to pass after transform carts of array to MerchantCardComponent, the type of the transformation I want is the array (carts) become :
let carts = [
{
merchantId: 1,
merchantName: 'Merchant A',
merchantLogo: 'https://assets.backend.com/merchants/1/logo_url.jpg',
items: [
{
productId: 1,
productName: 'Product One',
productImage: 'https://assets.backend.com/merchants/1/products/1/product_image.jpg',
productPrice: 2000,
productQty: 1,
},
{
productId: 2,
productName: 'Product Two',
productImage: 'https://assets.backend.com/merchants/1/products/2/product_image.jpg',
productPrice: 2000,
productQty: 5,
},
],
},
{
merchantId: 2,
merchantName: 'Merchant B',
merchantLogo: 'https://assets.backend.com/merchants/2/logo_url.jpg',
items: [
{
productId: 3,
productName: 'Product Three',
productImage: 'https://assets.backend.com/merchants/2/products/3/product_image.jpg',
productPrice: 2000,
productQty: 2,
},
{
productId: 4,
productName: 'Product Four',
productImage: 'https://assets.backend.com/merchants/4/products/4/product_image.jpg',
productPrice: 2000,
productQty: 4,
},
],
},
];
So that i can easily just print out the text, without fetch to backend again in the screen. I have make an seperate file for the transform the cart function, here is the code :
import React, { useContext, useState } from 'react';
import CartsContext from './CartsContext';
import {
doRequestMerchantDetail,
doRequestProductDetail,
} from '../merchants/MerchantsService';
export const tranformCartArray = async () => {
const [transformCart, setTransformCart] = useState([]);
const [afterTransform, setAfterTransform] = useState(null);
const [items, setItems] = useState([]);
const { carts } = useContext(CartsContext);
for (let i = 0; i < carts.length; i++) {
let [errMerchant, merchant] = await doRequestMerchantDetail(
carts[i].merchantId,
);
for (let j = 0; j < cart[i].items.length; j++) {
let [errProduct, product] = await doRequestProductDetail(
carts[i].items[j].productId,
);
if (product) {
setItems(
...items,
...[
{
productName: product.product_name,
productQty: carts[i].items[j].productQty,
productPrice: product.product_price,
productId: carts[i].items[j].productId,
productImage: product.product_image_url,
},
],
);
} else {
setItems(
...items,
...[
{
productName: 'Error',
productQty: carts[i].items[j].productQty,
productPrice: 'Error',
productId: carts[i].items[j].productId,
productImage:
'https://cdn.pixabay.com/photo/2021/07/21/12/49/error-6482984_960_720.png',
},
],
);
}
}
if (merchant) {
setAfterTransform({
merchantName: merchant.name,
merchantLogo: merchant.merchant_logo_url,
merchantId: carts[i].merchantId,
items,
});
} else {
setAfterTransform({
merchantName: 'Error',
merchantLogo: 'Error',
merchantId: carts[i].merchantId,
items,
});
}
setTransformCart(...transformCart, ...[afterTransform]);
}
return transformCart;
};
Until now i'm still confuse what make the warning show out, and the not working i was mention in the first sentence is the it return an empty array after the transform. Here is the screenshoot of console
Upvotes: 3
Views: 280
Reputation: 202667
tranformCartArray
isn't a react component or custom React hook so you can't use the useState
and useContext
hooks.
Additionally, the "unhandled rejection" was thrown because the getCarts
function is declared async
so it implicitly returns a Promise and when the React hooks error was thrown there was no catch
block or .catch
of a Promise chain to catch and handle it.
I would suggest passing the carts
state from the context into the tranformCartArray
utility function, and return an augmented carts array. This allows you to remove any and all hooks from the function.
export const tranformCartArray = async (carts) => {
const newCarts = [];
for (let i = 0; i < carts.length; i++) {
let [, merchant] = await doRequestMerchantDetail(
carts[i].merchantId,
);
const newItems = [];
for (let j = 0; j < carts[i].items.length; j++) {
let [, product] = await doRequestProductDetail(
carts[i].items[j].productId,
);
newItems.push({
productName: product?.product_name ?? 'Error',
productQty: carts[i].items[j].productQty,
productPrice: product?.product_price ?? 'Error',
productId: carts[i].items[j].productId,
productImage: carts[i].items[j].product_image_url,
});
}
newCarts.push({
merchantName: merchant?.name ?? 'Error',
merchantLogo: merchant?.merchant_logo_url ?? 'Error',
merchantId: carts[i].merchantId,
items: newItems,
});
}
return newCarts;
};
CartScreen
const CartScreen = ({ navigation }) => {
const [carts, setCarts] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const { carts: contextCarts } = useContext(CartsContext); // <-- access context
const getCarts = async () => {
setIsLoading(true);
try {
const response = await tranformCartArray(contextCarts); // <-- pass context carts value
setCarts(response);
} catch(error) {
// handle any errors?
} finally {
setIsLoading(false);
}
};
...
Upvotes: 3