How to re render component without changing state - reactjs

I have a component that fetch data first. then I manipulate the data but it doesn't change value until I change the screen and return back. Here I change the data based on the categories and doctors which i fetch using redux. but transformed data remains empty until i back to the page for the second time. Thanks for your help
import React, { useState, useEffect, useCallback } from "react";
import {
View,
Text,
StyleSheet,
ActivityIndicator,
FlatList,
Dimensions,
} from "react-native";
import { useSelector, useDispatch } from "react-redux";
import Colors from "../../constants/Colors";
import * as DoctorsActions from "../../store/actions/Doctors";
const { height } = Dimensions.get("window");
const ConcultationMainScreen = (props) => {
const dispatch = useDispatch();
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState("");
const categories = useSelector((state) => state.categories.categories);
const doctors = useSelector((state) => state.doctors.doctors);
const loadAllDoctors = useCallback(async () => {
try {
await dispatch(DoctorsActions.getDoctors());
} catch (err) {
setError(err);
console.log(error);
}
}, [dispatch, setError]);
useEffect(() => {
setIsLoading(true);
loadAllDoctors().then(() => {
setIsLoading(false);
});
}, [dispatch, loadAllDoctors]);
if (isLoading) {
return (
<View style={styles.centered}>
<ActivityIndicator size="large" color={Colors.blue} />
</View>
);
}
let transformedData = [];
for (const cat in categories) {
let doctorsOfCategory = [];
for (const doc in doctors) {
if (doctors[doc].categories[0] === categories[cat].name) {
doctorsOfCategory.push({
doctorName: doctors[doc].name,
});
} else {
continue;
}
}
transformedData.push({
categoryName: categories[cat].name,
doctorsOfCategory: doctorsOfCategory,
});
}
console.log(transformedData);
const renderConsultCategories = (ItemData) => {
return (
<View style={styles.item}>
<View style={styles.titleContainer}>
<Text>
گفتگو و مشاوره با متخصصین <Text>{ItemData.item.categoryName}</Text>
</Text>
</View>
</View>
);
};
return (
<View style={styles.screen}>
<FlatList
data={transformedData}
keyExtractor={(item) => item.categoryName}
renderItem={renderConsultCategories}
/>
</View>
);
};
export const screenOptions = (navData) => {
return {
headerTitle: "صفحه مشاوره",
headerTitleAlign: "center",
};
};
const styles = StyleSheet.create({
screen: {
flex: 1,
},
centered: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
item: {
flexDirection: "row-reverse",
width: "100%",
height: height / 8,
marginVertical: 10,
borderBottomWidth: 1,
},
});
export default ConcultationMainScreen;

Use this.forceUpdate() to re-render without calling setState().

Related

REACT NATIVE: fetching array of image from firestore database collection to carousel

Good day.
I have managed to fetch an array of images from firestore database collection, the only problem is that it seems that I cannot loop over the array that I am retrieving from the database.
I need to dynamically display my images on the carousel.
import React, { useState, useEffect, useCallback } from 'react';
import { Text, Dimensions, StyleSheet, View, Image } from 'react-native';
import { SwiperFlatList } from 'react-native-swiper-flatlist';
import firestore from '#react-native-firebase/firestore';
import { white } from 'react-native-paper/lib/typescript/styles/colors';
import { color } from 'react-native-reanimated';
import Carousel from 'react-native-reanimated-carousel';
//const colors = ["tomato", "thistle", "skyblue", "teal"];
const names = [{"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/Ads%2FJB.png?alt=media&token=377f1629-6807-4343-b826-93d1c2bc5de6"}, {"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/Ads%2Fgymtarp.png?alt=media&token=b2d1c923-2b5c-4066-bf8f-dc12de399059"}, {"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/photos%2Fb75d0848-b37f-4584-ab46-f355ca838e83.jpg?alt=media&token=adaa8559-de91-4ced-b056-84300123102e"}, {"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/Ads%2FSANDO.png?alt=media&token=274c946b-7f20-4c07-8545-431a0558257c"}];
const App = () => {
const [ads, setAds] = useState([]); // Initial empty array of ads
useEffect(() => {
const subscriber = firestore()
.collection('AdsDB')
//.orderBy('Menu', 'asc')
.onSnapshot(querySnapshot => {
//const ads = [];
/*
querySnapshot.forEach(documentSnapshot => {
ads.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
*/
querySnapshot.forEach(doc => {
const { AdImage } = doc.data();
ads.push({
//id: doc.id,
AdImage,
//Price,
});
});
setAds(ads);
console.log(ads);
//console.log(Object.entries(ads));
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}, []);
return (
<View style={styles.container}>
<SwiperFlatList
autoplay
autoplayDelay={2}
autoplayLoop
index={2}
showPagination
data={ads}
renderItem={({ item }) => (
<View style={[styles.child, { backgroundColor: item }]}>
<Text style={styles.text}>{item.AdImage}</Text>
<Image
style={{
width: "100%",
height: "30%",
position: 'absolute',
top:0,
alignItems: 'center',
justifyContent: 'center'}}
source={{uri : item.AdImage}}
resizeMode={'stretch'} // cover or contain its upto you view look
/>
</View>
)}
/>
</View>
)
};
const { width } = Dimensions.get('window');
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white', },
child: { width, justifyContent: 'center', height: '100%' },
text: { fontSize: width * 0.1, textAlign: 'center' },
});
export default App;
Thank you for answering my question. Mabuhay! I'm from the Philippines.
The problem I am seeing is that probably meanwhile you are receiving asynchronously the snapshots, the setState did not ended running (a race condition between the velocity receiving snapshots and the exact time needed to persist the state)
I would try to delay the state change and use the previous state value in the setter, like this:
useEffect(() => {.
const subscriber = firestore()
.collection('AdsDB')
.onSnapshot(querySnapshot => {
querySnapshot.forEach(doc => {
const { AdImage } = doc.data();
setTimeout(() => {
setAds((prevAds) => [...prevAds, AdImage]);
}, 500);
});
console.log(ads);
//console.log(Object.entries(adsFromFirebase));
});
These are my code, Sir. It gives an empty array.
import React, { useState, useEffect, useCallback } from 'react';
import { Text, Dimensions, StyleSheet, View, Image } from 'react-native';
import { SwiperFlatList } from 'react-native-swiper-flatlist';
import firestore from '#react-native-firebase/firestore';
import { white } from 'react-native-paper/lib/typescript/styles/colors';
import { color } from 'react-native-reanimated';
const colors = ["tomato", "thistle", "skyblue", "teal"];
const names = [{"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/Ads%2FJB.png?alt=media&token=377f1629-6807-4343-b826-93d1c2bc5de6"}, {"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/Ads%2Fgymtarp.png?alt=media&token=b2d1c923-2b5c-4066-bf8f-dc12de399059"}, {"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/photos%2Fb75d0848-b37f-4584-ab46-f355ca838e83.jpg?alt=media&token=adaa8559-de91-4ced-b056-84300123102e"}, {"AdImage": "https://firebasestorage.googleapis.com/v0/b/pindot-65e7c.appspot.com/o/Ads%2FSANDO.png?alt=media&token=274c946b-7f20-4c07-8545-431a0558257c"}];
const App = () => {
const [ads, setAds] = useState([]); // Initial empty array of ads
useEffect(() => {
const subscriber = firestore()
.collection('AdsDB')
.onSnapshot(querySnapshot => {
querySnapshot.forEach(doc => {
const { AdImage } = doc.data();
setTimeout(() => {
setAds((prevAds) => [...prevAds, AdImage]);
}, 500);
});
console.log(ads);
//console.log(Object.entries(adsFromFirebase));
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}, []);
/*
useEffect(() => {
const subscriber = firestore()
.collection('AdsDB')
//.orderBy('Menu', 'asc')
.onSnapshot(querySnapshot => {
//const ads = [];
/*
querySnapshot.forEach(documentSnapshot => {
ads.push({
...documentSnapshot.data(),
key: documentSnapshot.id,
});
});
querySnapshot.forEach(doc => {
const { AdImage } = doc.data();
ads.push({
//id: doc.id,
AdImage,
//Price,
});
});
setAds(ads);
//console.log(ads);
//console.log(Object.entries(ads));
});
// Unsubscribe from events when no longer in use
return () => subscriber();
}, []);
*/
return (
<View style={styles.container}>
<SwiperFlatList
autoplay
autoplayDelay={2}
autoplayLoop
index={2}
data={ads}
renderItem={({ item }) => (
<View style={[styles.child, { backgroundColor: item.colors }]}>
<Text style={styles.text}>{item.AdImage}</Text>
<Image
style={{
width: "100%",
height: "30%",
position: 'absolute',
top:0,
alignItems: 'center',
justifyContent: 'center'}}
source={{uri : item.AdImage}}
resizeMode={'stretch'} // cover or contain its upto you view look
/>
{/*}*/}
</View>
)}
/>
</View>
)
};
const { width } = Dimensions.get('window');
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white', },
child: { width, justifyContent: 'center', height: '100%' },
text: { fontSize: width * 0.1, textAlign: 'center' },
});
export default App;
This is the output.
LOG Running "myApp" with {"rootTag":21}
LOG []

React-Navigator automatically navigates back previous navigator

So i have a react navigation setup like this;
const WorldStackNavigator = createStackNavigator();
export const WorldNavigator = () => {
return (
<WorldStackNavigator.Navigator screenOptions={defaultNavOptions}>
<WorldStackNavigator.Screen
name='WorldCaseScreen'
component={WorldCaseScreen}
options={WorldCaseScreenOptions}
/>
</WorldStackNavigator.Navigator>
)
}
const FavouritesStackNavigator = createStackNavigator();
const FavouritesNavigator = () => {
return (
<FavouritesStackNavigator.Navigator screenOptions={defaultNavOptions} >
<FavouritesStackNavigator.Screen
name='FavouritesScreen'
component={FavouritesScreen}
options={FavouritesScreenOptions}
/>
<CountryStackNavigator.Screen
name='CountryDetailScreen'
component={CountryDetailScreen}
options={CountryDetailScreenOptions}
/>
</FavouritesStackNavigator.Navigator>
)
}
const CountryStackNavigator = createStackNavigator();
export const CountryNavigator = () => {
return (
<CountryStackNavigator.Navigator screenOptions={defaultNavOptions}>
<CountryStackNavigator.Screen
name='SearchbyContinentScreen'
component={SearchbyContinentScreen}
options={SearchbyContinentScreenOptions}
/>
<CountryStackNavigator.Screen
name='SearchIndividualCountryScreen'
component={SearchIndividualCountryScreen}
options={SearchIndividualScreenOptions}
/>
<CountryStackNavigator.Screen
name='CountryDetailScreen'
component={CountryDetailScreen}
options={CountryDetailScreenOptions}
/>
</CountryStackNavigator.Navigator>
)
}
And i group them all up in a tab navigator.
I'm using the 'CountryDetailScreen' in two different navigators.
So my problem is whenever 'CountryDetailScreen' is open in 'CountryNavigator' and i try to open 'CountryDetailScreen' in 'FavouritesNavigator' it navigates me back to 'CountryNavigator's 'CountryDetailScreen'. Then if i click on 'FavouritesNavigator' 'CountryDetailScreen' it comes with correct data.
Here is my CountryDetail code;
import { StyleSheet, Text, View, Image, ScrollView, Button, ActivityIndicator } from 'react-native'
import React, { useCallback, useEffect, useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { getIndividualCountryData, fetchFavourites, addToFav, removeFromFav } from '../store/actions/countryActions'
import Colors from '../constants/Colors';
import CaseInfoComp from '../components/CaseInfoComp';
import CustomHeaderButton from '../UI/CustomHeaderButton';
import { HeaderButtons, Item } from 'react-navigation-header-buttons';
const CountryDetailScreen = (props) => {
const data = props.route.params.data;
const dispatch = useDispatch();
const countriesReducer = useSelector(state => state.countryReducer)
const [error, setError] = useState(false);
const [isFav, setIsFav] = useState(false);
const [isFavLoading, setisFavLoading] = useState(true);
const [isInitial, setIsInitial] = useState(props.route.params.isInit);
const { singleCountry, favouriteCountries, loading } = countriesReducer;
let favHandlerDispatcher;
const loadIndividualCountryData = useCallback(async () => {
setError(false);
try {
await dispatch(getIndividualCountryData(data.Country, data.ThreeLetterSymbol));
} catch (error) {
setError(true)
}
}, [dispatch, setError])
const loadFavouriteCountries = useCallback(async () => {
setError(false);
try {
await dispatch(fetchFavourites());
} catch (error) {
setError(true)
}
}, [dispatch, setError])
useEffect(() => {
loadIndividualCountryData();
}, [loadIndividualCountryData])
useEffect(() => {
loadFavouriteCountries();
}, [])
useEffect(() => {
const favData = favouriteCountries.some((element) => element.countryCode === data.ThreeLetterSymbol)
setIsFav(favData)
favData ? favHandlerDispatcher = removeFromFav : favHandlerDispatcher = addToFav;
const favHandler = async (countryName, countryCode) => {
try {
setisFavLoading(true);
await dispatch(favHandlerDispatcher(countryName, countryCode));
await loadFavouriteCountries();
setisFavLoading(false);
} catch (error) {
throw error;
}
}
props.navigation.navigate('CountryDetailScreen', { data: data, favHandler: favHandler, isFav: favData, isFavLoading: isFavLoading })
}, [favouriteCountries])
if (loading || singleCountry.length === 0) {
return (
<View style={[styles.container, styles.horizontal]}>
<ActivityIndicator size="large" color={Colors.primary} />
</View>
)
} else if (error) {
return (
<View style={styles.container} >
<Text>Bir Hata Oluştu</Text>
<Button title='Tekrar deneyin' onPress={() => loadIndividualCountryData()} color={Colors.primary} />
</View>
)
} else {
return (
<ScrollView>
<View style={styles.imageContainer}>
<Image style={styles.image} source={{ uri: `https://countryflagsapi.com/png/${data.ThreeLetterSymbol}` }} />
</View>
{singleCountry.map((data) => {
return <CaseInfoComp header={data.header} content={data.content} color={data.color} key={data.header} />
})}
</ScrollView>
)
}
}
export default CountryDetailScreen
export const screenOptions = (navData) => {
const favHandler = navData.route.params.favHandler
const isFav = navData.route.params.isFav
const favLoading = navData.route.params.favLoading
return {
headerTitle: navData.route.params.data.Country,
headerRight: () => {
if (!favLoading) {
return (
<HeaderButtons HeaderButtonComponent={CustomHeaderButton} >
<Item title='Favourite' iconName={isFav ? 'star' : 'star-outline'} onPress={() => favHandler(navData.route.params.data.Country, navData.route.params.data.ThreeLetterSymbol)} />
</HeaderButtons>
)
}
}
}
}
const styles = StyleSheet.create({
imageContainer: {
flex: 1,
alignItems: 'center',
// borderBottomColor: Colors.primary,
// borderBottomWidth: 5
},
image: {
width: '100%',
height: 200,
borderBottomRightRadius: 30,
borderBottomLeftRadius: 30,
},
container: {
flex: 1,
justifyContent: "center",
alignItems: 'center'
}
})
How do i prevent it from going back to previous navigators screen.

How to aggregate values from a list and display in react native app?

I have a list which I get from firebase and displaying those values in the UI using SwipeListView.
The list data contains details like: coin price, count of coins purchased, total cost of the coins as price,count and amount. And some other fields.
Now, I want to make aggregate of all the coins and display in the text field. But the aggregation I am not able to set.
I tried creating two variables and setting those using hooks but getting error as
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
Can you please suggest the approach please. Below is my code.
import React, { useEffect, useState } from "react";
import {
FlatList,
Keyboard,
Text,
TextInput,
TouchableOpacity,
View,
StyleSheet,
} from "react-native";
import { Avatar, Button, Card, Title, Paragraph } from "react-native-paper";
import { SwipeListView } from "react-native-swipe-list-view";
import styles from "./styles";
import { firebase } from "../../firebase/config";
import { Icon } from "react-native-elements";
export default function OrderList(props) {
const LeftContent = (props) => <Avatar.Icon {...props} icon="folder" />;
const [orderText, setOrderText] = useState("");
const [orders, setOrders] = useState([]);
const orderRef = firebase.firestore().collection("orders");
const userID = props.route.params.userID;
const coin = props.route.params.coin;
let totalCost = 0;
let totalCount = 0;
const [totalCost, setTotalCost] = useState[""];
const [totalCount, setTotalCount] = useState[""];
const [averageValue, setAverageValue] = useState("");
//averageValue = (totalCost / totalCount).toString();
useEffect(() => {
orderRef
.where("authorID", "==", userID)
.where("name", "==", coin)
.orderBy("createdAt")
.onSnapshot(
(querySnapshot) => {
const newOrders = [];
querySnapshot.forEach((doc) => {
const order = doc.data();
order.id = doc.id;
newOrders.push(order);
});
setOrders(newOrders);
},
(error) => {
console.log(error);
}
);
}, []);
useEffect(() => {
setAverageValue(
(parseFloat(totalCost) / parseFloat(totalCount)).toString()
);
}, [totalCount, totalCost]);
const onAddButtonPress = () => {
props.navigation.navigate("CreateOrder", {
coin: coin,
userID: userID,
orderRef,
});
};
const renderOrder = ({ item, index }) => {
console.log("----------------------");
console.log(item.createdAt.toDate().toString());
console.log("----------------------");
setTotalCost(parseFloat(totalCost) + parseFloat(item.price));
setTotalCount(parseFloat(totalCount) + parseFloat(item.count));
console.log(totalCost);
console.log(totalCount);
return (
<View style={styles1.rowFront}>
<Text>
{index}. {item.price} {item.amount} {item.count}
{"\n" + item.createdAt.toDate().toString()}
</Text>
<Icon name={"flight-takeoff"} />
</View>
);
};
return (
<View style={styles.container}>
<TouchableOpacity style={styles.button} onPress={onAddButtonPress}>
<Text style={styles.buttonText}>Click here to create new order..</Text>
</TouchableOpacity>
<Text>Average: {averageValue}</Text>
{orders && (
<SwipeListView
data={orders}
keyExtractor={(item) => item.id}
renderItem={renderOrder}
removeClippedSubviews={true}
/>
)}
</View>
);
}
const styles1 = StyleSheet.create({
rowFront: {
alignItems: "center",
backgroundColor: "#FFF",
borderBottomWidth: 0.25,
justifyContent: "center",
height: 50,
},
rowBack: {
alignItems: "center",
backgroundColor: "#DDD",
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
paddingLeft: 15,
},
backRightBtn: {
alignItems: "center",
bottom: 0,
justifyContent: "center",
position: "absolute",
top: 0,
width: 75,
backgroundColor: "red",
right: 0,
},
});
Modified code with normal variables and useEffect linked to them to change average.
import React, { useEffect, useState } from "react";
import {
FlatList,
Keyboard,
Text,
TextInput,
TouchableOpacity,
View,
StyleSheet,
} from "react-native";
import { Avatar, Button, Card, Title, Paragraph } from "react-native-paper";
import { SwipeListView } from "react-native-swipe-list-view";
import styles from "./styles";
import { firebase } from "../../firebase/config";
import { Icon } from "react-native-elements";
export default function OrderList(props) {
const LeftContent = (props) => <Avatar.Icon {...props} icon="folder" />;
const [orderText, setOrderText] = useState("");
const [orders, setOrders] = useState([]);
const orderRef = firebase.firestore().collection("orders");
const userID = props.route.params.userID;
const coin = props.route.params.coin;
let totalCost = 0;
let totalCount = 0;
//const [totalCost, setTotalCost] = useState[""];
//const [totalCount, setTotalCount] = useState[""];
const [averageValue, setAverageValue] = useState("");
//averageValue = (totalCost / totalCount).toString();
useEffect(() => {
orderRef
.where("authorID", "==", userID)
.where("name", "==", coin)
.orderBy("createdAt")
.onSnapshot(
(querySnapshot) => {
const newOrders = [];
querySnapshot.forEach((doc) => {
const order = doc.data();
order.id = doc.id;
newOrders.push(order);
});
setOrders(newOrders);
},
(error) => {
console.log(error);
}
);
}, []);
useEffect(() => {
setAverageValue(
(parseFloat(totalCost) / parseFloat(totalCount)).toString()
);
}, [totalCount, totalCost]);
const onAddButtonPress = () => {
props.navigation.navigate("CreateOrder", {
coin: coin,
userID: userID,
orderRef,
});
};
const renderOrder = ({ item, index }) => {
console.log("----------------------");
console.log(item.createdAt.toDate().toString());
console.log("----------------------");
//setTotalCost(parseFloat(totalCost) + parseFloat(item.price));
//setTotalCount(parseFloat(totalCount) + parseFloat(item.count));
totalCost = parseFloat(totalCost) + parseFloat(item.price);
totalCount = parseFloat(totalCount) + parseFloat(item.count);
console.log(totalCost);
console.log(totalCount);
return (
<View style={styles1.rowFront}>
<Text>
{index}. {item.price} {item.amount} {item.count}
{"\n" + item.createdAt.toDate().toString()}
</Text>
<Icon name={"flight-takeoff"} />
</View>
);
};
return (
<View style={styles.container}>
<TouchableOpacity style={styles.button} onPress={onAddButtonPress}>
<Text style={styles.buttonText}>Click here to create new order..</Text>
</TouchableOpacity>
<Text>Average: {averageValue}</Text>
{orders && (
<SwipeListView
data={orders}
keyExtractor={(item) => item.id}
renderItem={renderOrder}
removeClippedSubviews={true}
/>
)}
</View>
);
}
const styles1 = StyleSheet.create({
rowFront: {
alignItems: "center",
backgroundColor: "#FFF",
borderBottomWidth: 0.25,
justifyContent: "center",
height: 50,
},
rowBack: {
alignItems: "center",
backgroundColor: "#DDD",
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
paddingLeft: 15,
},
backRightBtn: {
alignItems: "center",
bottom: 0,
justifyContent: "center",
position: "absolute",
top: 0,
width: 75,
backgroundColor: "red",
right: 0,
},
});
.
thanks #jnpdx it is working now. Based on your inputs, my changes.
import React, { useEffect, useState } from "react";
import {
FlatList,
Keyboard,
Text,
TextInput,
TouchableOpacity,
View,
StyleSheet,
} from "react-native";
import { Avatar, Button, Card, Title, Paragraph } from "react-native-paper";
import { SwipeListView } from "react-native-swipe-list-view";
import styles from "./styles";
import { firebase } from "../../firebase/config";
import { Icon } from "react-native-elements";
import { createIconSetFromFontello } from "#expo/vector-icons";
export default function OrderList(props) {
const LeftContent = (props) => <Avatar.Icon {...props} icon="folder" />;
const [orderText, setOrderText] = useState("");
const [orders, setOrders] = useState([]);
const orderRef = firebase.firestore().collection("orders");
const userID = props.route.params.userID;
const coin = props.route.params.coin;
//averageValue = (totalCost / totalCount).toString();
const [averageValue, setAverageValue] = useState("");
useEffect(() => {
orderRef
.where("authorID", "==", userID)
.where("name", "==", coin)
.orderBy("createdAt")
.onSnapshot(
(querySnapshot) => {
const newOrders = [];
querySnapshot.forEach((doc) => {
const order = doc.data();
order.id = doc.id;
newOrders.push(order);
});
setOrders(newOrders);
},
(error) => {
console.log(error);
}
);
}, []);
useEffect(() => {
//calculate and set anything like totalCost, averageValue, etc here
console.log("---came to orders effect---");
//console.log(orders);
let totalCost = 0;
let totalCount = 0;
orders.forEach((item, index) => {
console.log(item);
console.log(index);
totalCost += parseFloat(item.amount);
totalCount += parseFloat(item.count);
});
setAverageValue((totalCost / totalCount).toString());
}, [orders]);
/*
useEffect(() => {
let avg = (parseFloat(totalCost) / parseFloat(totalCount)).toString();
console.log("Avg:" + avg);
setAverageValue(avg);
}, [totalCount, totalCost]);
*/
const onAddButtonPress = () => {
props.navigation.navigate("CreateOrder", {
coin: coin,
userID: userID,
orderRef,
});
};
const renderOrder = ({ item, index }) => {
//console.log("----------------------");
//console.log(item.createdAt.toDate().toString());
//console.log("----------------------");
//setTotalCost(parseFloat(totalCost) + parseFloat(item.price));
//setTotalCount(parseFloat(totalCount) + parseFloat(item.count));
//totalCost = parseFloat(totalCost) + parseFloat(item.price);
//totalCount = parseFloat(totalCount) + parseFloat(item.count);
//console.log(totalCost);
//console.log(totalCount);
return (
<View style={styles1.rowFront}>
<Text>
{index}. {item.price} {item.amount} {item.count}
{"\n" + item.createdAt.toDate().toString()}
</Text>
<Icon name={"flight-takeoff"} />
</View>
);
};
return (
<View style={styles.container}>
<TouchableOpacity style={styles.button} onPress={onAddButtonPress}>
<Text style={styles.buttonText}>Click here to create new order..</Text>
</TouchableOpacity>
{orders.length === 0 && (
<Text>Please order some coins in currency: {coin}</Text>
)}
{orders && orders.length > 0 && (
<>
<Text>Average Value: {averageValue}</Text>
<SwipeListView
data={orders}
keyExtractor={(item) => item.id}
renderItem={renderOrder}
removeClippedSubviews={true}
/>
</>
)}
</View>
);
}
const styles1 = StyleSheet.create({
rowFront: {
alignItems: "center",
backgroundColor: "#FFF",
borderBottomWidth: 0.25,
justifyContent: "center",
height: 50,
},
rowBack: {
alignItems: "center",
backgroundColor: "#DDD",
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
paddingLeft: 15,
},
backRightBtn: {
alignItems: "center",
bottom: 0,
justifyContent: "center",
position: "absolute",
top: 0,
width: 75,
backgroundColor: "red",
right: 0,
},
});
You're calling useState hook functions (setTotalCost and setTotalCount) inside your render function. This will end up causing issues like the error that you're seeing or even worse, an infinite loop.
Instead of calling these when rendering, use useEffect instead and perform the necessarily calculations whenever the related dependencies change. It looks like you're already doing that for setAverageValue, so you're familiar with what's involved in doing that.
Update, based on comments:
useEffect(() => {
//calculate and set anything like totalCost, averageValue, etc here
}, [orders])

useEffect: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application

Hi I am having an issue in my react native expo app when I updated the APK version from 28 to 29 - for google plays new update.
Heres my error after dependancy updates in the component specified in the error log below:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s, a useEffect cleanup function
I not sure exactly where the problem lies in the code
Code with useEffect cleanup attempt:
//React Deps
import React, { useState, useEffect, useCallback } from "react";
import {
View,
StyleSheet,
Image,
Text,
Platform,
Button,
ActivityIndicator,
} from "react-native";
import { SearchBar } from "react-native-elements";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import { useSelector, useDispatch } from "react-redux";
//Components
import HeaderButton from "../components/HeaderButton";
import JokesList from "../components/JokesList";
//Misc
import Colors from "../constants/Colors";
//Store
import * as jokeAction from "../store/actions/jokes-action";
const HomeScreen = (props) => {
const jokes = useSelector((state) => state.jokes.jokes);
const [search, setIsSearch] = useState(""); //Search
const [newSearch, setIsNewSearch] = useState([]); //New Search
const [isFetching, setIsFetchingFav] = useState(false); //Fav
const [isLoading, setIsLoading] = useState(); //Loading
const [isRefreshing, setIsRefreshing] = useState(false); //Refreshing
const [error, setError] = useState(""); //Errors
const dispatch = useDispatch();
//Load Jokes
useEffect(() => {
let isCancelled = false; //attempt
console.log("load jokes");
setIsLoading(true);
loadJokes().then(() => {
if (!isCancelled) {
setIsLoading(false);
console.log("set is loading to false");
}
});
//Cleanup ?
return () => {
isCancelled = true;
};
}, [dispatch, loadJokes]); //Deps
//Loading All Jokes
const loadJokes = useCallback(async () => {
console.log("enter load jokes func");
setError(null);
setIsRefreshing(true);
try {
await dispatch(jokeAction.fetchJokes());
} catch (err) {
setError(err.message);
console.log("error");
}
setIsRefreshing(false);
}, [dispatch, setIsLoading, setError]);
//Update after render
useEffect(() => {
console.log("update after render");
const willFocusSub = props.navigation.addListener("willFocus", loadJokes);
return () => {
willFocusSub.remove();
};
}, [loadJokes]);
//Load Fav Jokes
useEffect(() => {
console.log("load fav jokes");
fetchingFavJokes();
}, [dispatch, fetchingFavJokes]);
//Favs
const fetchingFavJokes = useCallback(async () => {
setError(null);
try {
await dispatch(jokeAction.loadFavJokes());
} catch (err) {
setError(err.message);
}
setIsFetchingFav(false);
}, [dispatch, setIsFetchingFav, setError]);
//Events
const updateSearch = (text) => {
setIsSearch(text.toUpperCase());
const newData = jokes.filter((joke) => {
/* When no search text present, do not apply filtering */
if (!text) {
return true;
}
return (
typeof joke.title.toUpperCase() === "string" &&
joke.title.toUpperCase().includes(text.toUpperCase())
);
});
setIsNewSearch(newData);
};
//Loading Spinner
if (isLoading) {
return (
<View style={{ ...styles.screen, ...styles.centered }}>
<ActivityIndicator size="large" color="white" />
</View>
);
}
//Error
if (error) {
return (
<View style={{ ...styles.screen, ...styles.centered }}>
<Text style={{ color: "white" }}>An Error Occurred!</Text>
<Button
title="Try Again"
onPress={loadJokes}
color={Colors.accentColor}
/>
</View>
);
}
//Fall Back Text
if (!isLoading && jokes.length === 0) {
return (
<View style={styles.centered}>
<Text>No Jokes Found</Text>
</View>
);
}
return (
//Logo
<View style={styles.screen}>
<View style={styles.logoContainer}>
<Image style={styles.logo} source={require("../assets/logo.png")} />
</View>
<View style={styles.searchBar}>
<SearchBar
containerStyle={{
backgroundColor: "white",
borderTopColor: "white",
borderBottomColor: "white",
height: 50,
alignItems: "center",
justifyContent: "center",
}}
inputContainerStyle={{ backgroundColor: "white", height: 40 }}
inputStyle={{
backgroundColor: "white",
color: "#232323",
fontSize: 16,
fontFamily: "luckiest-guy",
}}
lightTheme
leftIconContainerStyle={{ backgroundColor: "white" }}
searchIcon={{ size: 24 }}
onChangeText={(text) => {
updateSearch(text);
}}
onClear={(text) => setIsSearch("")}
placeholder="Keyword or Topic..."
value={search}
/>
</View>
<JokesList
listData={!search ? jokes : newSearch}
onRefreshing={loadJokes}
isRefreshing={isRefreshing}
navigation={props.navigation}
/>
</View>
);
};
...Navigation/Styles etc
export default HomeScreen;

[Unhandled promise rejection: TypeError: undefined is not an object (evaluating '_expoMediaLibrary.MediaLibrary.createAssetAsync')]

I using expo-camera to record video and save it in my device, but even on stop recording it has video uri, the .MediaLibrary.createAssetAsync is giving the above error, also the recording gets stopped from the first even if I have used setTimeout to stop the recording, this is my code for the camera:
import React, { useState, useEffect, useRef } from 'react';
import { Text, View } from 'react-native';
import { Camera } from 'expo-camera';
import { MediaLibrary } from 'expo-media-library';
import { Audio } from 'expo-av';
export default function App() {
const [hasPermission, setHasPermission] = useState(null);
const [camera, setCamera] = useState(null);
const [recording, setRecording] = useState(false);
const [video, setVideo] = useState(null);
const [stop, setStop] = useState(false);
const recordingVideo = async () => {
const video = await camera.recordAsync();
console.log(video);
setVideo(video);
}
const saveVideo = async () => {
console.log('uri', video.uri);
const asset = await MediaLibrary.createAssetAsync(video.uri);
if (asset) {
console.log('asset', asset);
setVideo(null);
}
};
useEffect(() => {
console.log('recoring', recording);
if (recording && camera) recordingVideo();
}, [recording, camera]);
useEffect(() => {
console.log('stop', stop);
if (stop) {
setRecording(false);
camera.stopRecording();
saveVideo();
}
}, [stop]);
useEffect(() => {
(async () => {
const { status } = await Camera.requestPermissionsAsync();
const audioPerm = await Audio.requestPermissionsAsync();
setHasPermission(status === 'granted' && audioPerm.status === 'granted');
})();
}, []);
useEffect(() => {
if(camera) {
console.log('ref');
setRecording(true);
setTimeout(() => {
setStop(true);
}, 10000);
}
}, [camera]);
if (hasPermission === null) {
return <View />;
}
if (hasPermission === false) {
return <Text>No access to camera or audio</Text>;
}
return (
<View style={{ flex: 1 }}>
<Camera style={{ flex: 1 }} type={Camera.Constants.Type.front} ref={ref => setCamera(ref)}>
<View style={{ flex: 1, backgroundColor: '#00000000', justifyContent: 'center' }}>
<Text style={{ marginHorizontal: 40 }}>{recording ? 'Recording' : !stop ? 'Waiting for recording' : 'recording finished' }</Text>
</View>
</Camera>
</View>
);
}
I searched in expo-media-library docs and logged the video.uri that is exactly some matching parameter but cannot understand why it is working like that.
I was having the same problem in my Camera App. I solved by changing my import
// Change from this
import { MediaLibrary } from 'expo-media-library';
// To this
import * as MediaLibrary from 'expo-media-library';

Resources