Create simple custom progression bar in React - reactjs

I'm trying to create my simple progression bar in React using CSS and setInterval. It's not working properly after 10%. Does anyone know why it is happening? Thanks
import React, {useState, useEffect} from 'react';
const Loading = () => {
const [percentage, setPercentage] = useState(0);
const containerStyles = {
height: 20,
width: '100%',
backgroundColor: "#e0e0de",
borderRadius: 50,
margin: 50
}
const fillerStyles = {
height: '100%',
width: `${percentage.toString()}%`,
backgroundColor: 'red',
borderRadius: 'inherit',
textAlign: 'right'
}
const labelStyles = {
padding: 5,
color: 'white',
fontWeight: 'bold'
}
useEffect(() => {
const newPercentage = parseInt(percentage) + 1;
setInterval(() => setPercentage(newPercentage), 1000);
}, [percentage])
return (
<div style={containerStyles}>
<div style={fillerStyles}>
<span style={labelStyles}>{percentage}%</span>
</div>
</div>
)
}
export default Loading;

You should store your interval in a constant and use the cleanup function to clear the last interval each time.
I'd also change how you handle setPercentage and use timeout instead of interval
Something like this:
useEffect(() => {
const timeoutID = setTimeout(() =>
setPercentage(prevPercentage => prevPercentage + 1)
, 1000);
return () => clearTimeout(timeoutID);
}, [setPercentage]);

Related

making the position of a popconfirm static

I am using a popconfirm to show up when trying to close a modal, I want the popconfirm position to be below the X button of the modal, I have tried to move its position with the "overlaystyle" property and if I edit the values while the popconfirm is open it works, but if the screen dimensions change or the popconfirm is closed.
it returns to its original position, is there any way to make it stay below the X of the modal?
import { ExclamationCircleFilled } from "#ant-design/icons";
import { Popconfirm } from "antd";
import { useEffect, useState } from "react";
type EditModalCloseType = {
width: number
popupOpen: boolean;
onConfirm: () => void;
onCancel: () => void;
};
export const EditModalClose = ({width, popupOpen, onConfirm, onCancel }: EditModalCloseType) => {
useEffect(() => {
calculateOverlayStyle(width);
}, [width]);
const description = "¿Estás seguro de salir sin actualizar? los cambios se perderan"
const calculateOverlayStyle = (width: number) => {
setOverlayStyle({
...(width < 688 && { marginLeft: '20px', marginTop: '-160px' }),
...(width < 1300 && { position: 'absolute', top: '50%', left: '50%' }),
...(width >= 1300 && { position: 'absolute', top: '50%', left: '50%' })
});
}
const [overlayStyle, setOverlayStyle] = useState({});
return <Popconfirm
title={description}
open={popupOpen}
placement="bottom"
autoAdjustOverflow={true}
overlayStyle={overlayStyle}
icon={<ExclamationCircleFilled style={{ color: 'red' }} />}
okText="Salir"
okButtonProps={{ style: { backgroundColor: 'red', color: 'white' } }}
onConfirm={onConfirm}
cancelText="Regresar"
onCancel={onCancel}
/>
}
const PopupCancel = () => {
setPopupOpen(false);
};
const PopupConfirm = () => {
resetFields();
setPopupOpen(false);
setOpenModal(false);
}
I tried to create a function according to the width of the screen that would adjust it according to that property but the same error I mentioned in the post keeps occurring.

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 []

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])

react-native: How to create custom component dynamically and destroy it after use?

I am creating a screen in functional component where I have to execute an animation when there is any event occurs ... This event could occur 1000 times when screen is open ... so I have implemented a custom component which takes position on screen and animates ....
const FloatingComponent = (props) => {
const animationView = useSharedValue(1)
const animationOpacityView = useSharedValue(1)
const animationViewStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateY: withTiming(animationView.value, {
duration: 3500
}),
}
],
opacity: withTiming(animationOpacityView.value, {
duration: 1500
})
}
})
useEffect(() => {
console.log('Component animation called')
animationView.value = -((Screen.width * 0.25))
animationOpacityView.value = 0
});
return (
<Animated.View style={[Styles.handImg, { top: props.topDistance }, animationViewStyle]}>
<Image
style={Styles.handImage}
source={require('../../../assets/images/hand.png')}
/>
</Animated.View>
);
};
To create it dynamically I implemented it like this
const driverFactory = (itemNumber) => {
console.log(itemNumber)
return (<FloatingComponent id={1} topDistance={(Screen.width * 0.25) * itemNumber} />);
};
but it never show up and executes ....
while if I add this
<FloatingDriver id={5} topDistance={(Screen.width * 0.25) * 6} />
to main screen return it always executes .... but by these I can not create n number of components at any time when i receive notification ...
I would have a parent component manage these n child components and their rendering. Here is an example showing how one might render all the components with animations and handle showing/removing them as needed.
import React from 'react';
import {Animated, Button, SafeAreaView, StyleSheet} from 'react-native';
const Particle = ({particle: {x, y}, onFinish}) => {
const opacity = React.useRef(new Animated.Value(0)).current;
React.useEffect(() => {
Animated.timing(opacity, {
toValue: 1,
duration: 2000,
useNativeDriver: true,
}).start(() => {
onFinish();
});
}, []);
return (
<Animated.View
style={{
position: 'absolute',
left: x - 10,
bottom: y - 10,
width: 20,
height: 20,
backgroundColor: 'yellow',
opacity,
}}
/>
);
};
const ParticleSystem = () => {
const [layoutRectangle, setLayoutRectangle] = React.useState();
const [particleList, setParticleList] = React.useState([]);
const currentRef = React.useRef();
const addRandomParticle = React.useCallback(() => {
if (layoutRectangle) {
const newParticle = {
id: Math.floor(Math.random() * Number.MAX_SAFE_INTEGER),
x: Math.random() * layoutRectangle.width,
y: Math.random() * layoutRectangle.height,
};
setParticleList([newParticle, ...particleList]);
}
}, [layoutRectangle, particleList]);
const removeParticle = React.useCallback(
(particle) => {
setParticleList(particleList.filter(({id}) => id !== particle.id));
},
[particleList],
);
currentRef.current = removeParticle;
return (
<SafeAreaView
style={styles.screen}
onLayout={(event) => setLayoutRectangle(event.nativeEvent.layout)}>
{particleList.map((particle) => (
<Particle
key={particle.id}
particle={particle}
onFinish={() => {
currentRef.current(particle);
}}
/>
))}
<Button title="Add particle" onPress={() => addRandomParticle()} />
</SafeAreaView>
);
};
export default ParticleSystem;
const styles = StyleSheet.create({
screen: {
flex: 1,
backgroundColor: 'black',
},
});

how to change jss dynamically

I know many ways to do this without JSS, but this paradigm seems to make it very difficult:
const Backdrop = () => {
const {height, width} = useWindowSize()
const css = makeStyles(() => createStyles({
root : {
backgroundColor: 'black',
backgroundImage: 'url(/img/bg.jpg)',
height, // this value should update when window size changes
width,
position:'fixed',
}
}))()
return <div className={css.root}>
</div>
}
Update:
I copy-pasted myself into a way to make this work, but it's just too much code compared to traditional CSS
const [_windowSize, $_windowSize] = useState({ width: window.innerWidth, height: window.innerHeight })
const handler = () => $_windowSize({ width: window.innerWidth, height: window.innerHeight })
useEffect(() => {
window.addEventListener("resize", handler)
return () => window.removeEventListener("resize", handler)
}, [])
return _windowSize
}
const Backdrop = () => {
useWindowSize()
const css = makeStyles(() => createStyles({
root : {
backgroundColor: 'black',
backgroundImage: 'url(/img/bg.jpg)',
backgroundSize: 'cover',
height: () => window.innerHeight,
width: () => window.innerWidth,
position:'fixed',
}
}))()
return <div className={css.root}></div>
}
Somehow makeStyles knows of every update in the DOM. It's really unintuitive.
Might this suit you're need?
const { height, width } = useWindowDimensions();
const style = {
root : {
backgroundColor: 'black',
backgroundImage: 'url(/img/bg.jpg)',
backgroundSize: 'cover',
height: height,
width: width,
position:'fixed'
}
}
return <div style={style.root}></div>
useWindowDimensions is a custom effect defined like this:
/**
* Returns window dimensions, listening to resize event.
*
* Example:
*
* const Component = () => {
* const { height, width } = useWindowDimensions();
* }
*/
export function useWindowDimensions() {
const [windowDimensions, setWindowDimensions] = useState(
getWindowDimensions()
);
useEffect(() => {
function handleResize() {
setWindowDimensions(getWindowDimensions());
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return windowDimensions;
}
the effect listen to window resize and update the {width, height} state causing a clean rerendering.

Resources