Add event to selected day Calendar Agenda - javascript

I am currently learning to build an app with react native expo and javascript. I have my calendar displaying but I can't figure out how to add event to day selected. There will be a button that the user press and from this they should be able to add events to that day. This was the guide used to build the agenda https://github.com/wix/react-native-calendars/blob/master/example/src/screens/agendaScreen.tsx
import React, { Component, useState } from "react";
import { Alert, StyleSheet, Text, View, TouchableOpacity } from "react-native";
import {
Agenda,
DateData,
AgendaEntry,
AgendaSchedule,
} from "react-native-calendars";
import { Card } from "react-native-paper";
const timeToString = (time) => {
const date = new Date(time);
return date.toISOString().split("T")[0];
};
const CalendarComponent = () => {
const [entree, setItems] = useState({});
const loadItems = (day) => {
setTimeout(() => {
for (let i = -15; i < 85; i++) {
const time = day.timestamp + i * 24 * 60 * 60 * 1000;
const strTime = timeToString(time);
if (!entree[strTime]) {
entree[strTime] = [];
}
}
const newItems = {};
Object.keys(entree).forEach((key) => {
newItems[key] = entree[key];
});
setItems(newItems);
}, 1000);
};
const renderEmptyDate = () => {
return (
<View style={styles.emptyDate}>
<Text>This is empty date!</Text>
</View>
);
};
const renderItem = (item) => {
return (
<TouchableOpacity style={styles.item}>
<Card>
<Card.Content>
<View>
<Text>{item.name}</Text>
</View>
</Card.Content>
</Card>
</TouchableOpacity>
);
};
return (
<Agenda
items={entree}
loadItemsForMonth={loadItems}
renderItem={renderItem}
renderEmptyDate={renderEmptyDate}
showClosingKnob={true}
/>
);
};
const styles = StyleSheet.create({
item: {
backgroundColor: "white",
flex: 1,
borderRadius: 5,
padding: 10,
marginRight: 10,
marginTop: 17,
},
emptyDate: {
height: 15,
flex: 1,
paddingTop: 30,
},
});
export default CalendarComponent;

Related

how to make an infinite image carousel with hooks in react native

I am making an infinite image carousel using hooks and javascript in react native.
I am almost done with the code and also the carousel works absolutely fine but the active dots below the carousel runs faster than the images. I have tried a couple of things but unfortunately it didn't worked out.
Thanks in advance.
import React, {
useEffect,
useState,
useRef,
useCallback,
createRef,
} from 'react';
import {
StyleSheet,
View,
Dimensions,
FlatList,
LayoutAnimation,
UIManager,
Text,
} from 'react-native';
import {ActivityIndicator} from 'react-native';
import {Image} from 'react-native-elements';
const HomeCarousel = ({data}) => {
const [dimension, setDimension] = useState(Dimensions.get('window'));
const [index, setIndex] = useState(0);
const [dataState, setDataState] = useState(data);
slider = createRef();
let intervalId = null;
const onChange = () => {
setDimension(Dimensions.get('window'));
};
useEffect(() => {
Dimensions.addEventListener('change', onChange);
return () => {
Dimensions.removeEventListener('change', onChange);
};
});
useEffect(() => {
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}, []);
viewabilityConfig = {
viewAreaCoveragePercentThreshold: 50,
};
const onViewableItemsChanged = ({viewableItems, changed}) => {
if (viewableItems.length > 0) {
let currentIndex = viewableItems[0].index;
if (currentIndex % data.length === data.length - 1) {
setIndex(currentIndex),
setDataState(dataState => [...dataState, ...data]);
} else {
console.log(currentIndex, 'else');
setIndex(currentIndex);
}
}
};
const onSlideChange = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeIn);
const newIndex = index + 1;
setIndex(newIndex);
slider?.current?.scrollToIndex({
index: index,
animated: true,
});
};
const startInterval = useCallback(() => {
intervalId = setInterval(onSlideChange, 3000);
}, [onSlideChange]);
useEffect(() => {
startInterval();
return () => {
clearInterval(intervalId);
};
}, [onSlideChange]);
const viewabilityConfigCallbackPairs = useRef([
{viewabilityConfig, onViewableItemsChanged},
]);
const renderIndicator = ()=>{
const indicators = [];
data.map((val,key)=>(
indicators.push(
<Text
key={key}
style={
key === index % data.length ? {color: 'lightblue',fontSize:10,marginBottom: 8,marginHorizontal:1} :
{color: '#888',fontSize:10,marginBottom: 8,marginHorizontal:1}
}>
⬤
</Text>
)
));
return indicators;
}
return (
<View style={{width: dimension.width,height: 280, backgroundColor: '#fff'}}>
<FlatList
ref={slider}
horizontal
pagingEnabled
snapToInterval={dimension?.width}
decelerationRate="fast"
bounces={false}
showsHorizontalScrollIndicator={false}
data={dataState}
renderItem={({item, index}) => (
<>
<View>
<Image
source={{uri: `${item.url}`}}
style={{
width: dimension?.width,
height: 250,
resizeMode: 'cover',
}}
PlaceholderContent={<ActivityIndicator />}
/>
</View>
</>
)}
viewabilityConfigCallbackPairs={viewabilityConfigCallbackPairs.current}
getItemLayout={(data, index) => ({
length: dimension?.width,
offset: dimension?.width * index,
index,
})}
windowSize={1}
initialNumToRender={1}
maxToRenderPerBatch={1}
removeClippedSubviews={true}
/>
<View
style={{
flexDirection: 'row',
position: 'absolute',
bottom: 0,
alignSelf: 'center',
}}>
{renderIndicator()}
</View>
</View>
);
};
const styles = StyleSheet.create({});
export default HomeCarousel;
The data that is passed as the props to this component is
export const carouselImages = [
{url: 'https://i.ibb.co/FDwNR9d/img1.jpg'},
{url: 'https://i.ibb.co/7G5qqGY/1.jpg'},
{url: 'https://i.ibb.co/Jx7xqf4/pexels-august-de-richelieu-4427816.jpg'},
{url: 'https://i.ibb.co/GV08J9f/pexels-pixabay-267202.jpg'},
{url: 'https://i.ibb.co/sK92ZhC/pexels-karolina-grabowska-4210860.jpg'},
];
Ooooooh, I fixed it myself
Here is the perfectly working code full code. 😄
import React, {
useEffect,
useState,
useRef,
useCallback,
createRef,
} from 'react';
import {
StyleSheet,
View,
Dimensions,
FlatList,
LayoutAnimation,
UIManager,
Text,
} from 'react-native';
import {ActivityIndicator} from 'react-native';
import {Image} from 'react-native-elements';
const HomeCarousel = ({data}) => {
const [dimension, setDimension] = useState(Dimensions.get('window'));
const [index, setIndex] = useState(0);
const [dataState, setDataState] = useState(data);
const [indicatorIndex, setindicatorIndex] = useState();
slider = createRef();
let intervalId = null;
const onChange = () => {
setDimension(Dimensions.get('window'));
};
useEffect(() => {
Dimensions.addEventListener('change', onChange);
return () => {
Dimensions.removeEventListener('change', onChange);
};
});
useEffect(() => {
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}, []);
viewabilityConfig = {
viewAreaCoveragePercentThreshold: 50,
};
const onViewableItemsChanged = ({viewableItems, changed}) => {
if (viewableItems.length > 0) {
let currentIndex = viewableItems[0].index;
if (currentIndex % data.length === data.length - 1) {
setIndex(currentIndex), setindicatorIndex(currentIndex);
setDataState(dataState => [...dataState, ...data]);
} else {
console.log(currentIndex, 'else');
setIndex(currentIndex);
setindicatorIndex(currentIndex);
}
}
};
const onSlideChange = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeIn);
const newIndex = index + 1;
setIndex(newIndex);
slider?.current?.scrollToIndex({
index: index,
animated: true,
});
};
const startInterval = useCallback(() => {
intervalId = setInterval(onSlideChange, 3000);
}, [onSlideChange]);
useEffect(() => {
startInterval();
return () => {
clearInterval(intervalId);
};
}, [onSlideChange]);
const viewabilityConfigCallbackPairs = useRef([
{viewabilityConfig, onViewableItemsChanged},
]);
const renderIndicator = () => {
const indicators = [];
data.map((val, key) =>
indicators.push(
<Text
key={key}
style={
key === indicatorIndex % data.length
? {
color: 'lightblue',
fontSize: 10,
marginBottom: 8,
marginHorizontal: 1,
}
: {
color: '#888',
fontSize: 10,
marginBottom: 8,
marginHorizontal: 1,
}
}>
⬤
</Text>,
),
);
return indicators;
};
return (
<View
style={{width: dimension.width, height: 280, backgroundColor: '#fff'}}>
<FlatList
ref={slider}
horizontal
pagingEnabled
snapToInterval={dimension?.width}
decelerationRate="fast"
bounces={false}
showsHorizontalScrollIndicator={false}
data={dataState}
renderItem={({item, index}) => (
<>
<View>
<Image
source={{uri: `${item.url}`}}
style={{
width: dimension?.width,
height: 250,
resizeMode: 'cover',
}}
PlaceholderContent={<ActivityIndicator />}
/>
</View>
</>
)}
viewabilityConfigCallbackPairs={viewabilityConfigCallbackPairs.current}
getItemLayout={(data, index) => ({
length: dimension?.width,
offset: dimension?.width * index,
index,
})}
windowSize={1}
initialNumToRender={1}
maxToRenderPerBatch={1}
removeClippedSubviews={true}
/>
<View
style={{
flexDirection: 'row',
position: 'absolute',
bottom: 0,
alignSelf: 'center',
}}>
{renderIndicator()}
</View>
</View>
);
};
const styles = StyleSheet.create({});
export default HomeCarousel;

React native refreshControl ScrollView bottom-sheet ios

I am using the following component bottom-sheet, where I am implementing the pull-to-refresh, on android it works perfectly on ios it is not working.
I tried to follow various guides, but I could not figure out what the problem is that on ios it is not working.
Can anyone help me out?
Android ok:
Link: snack
import React, { useCallback, useRef, useMemo, useEffect } from 'react';
import {
StyleSheet,
View,
Text,
Button,
RefreshControl,
ToastAndroid,
Platform,
} from 'react-native';
import BottomSheet, { BottomSheetScrollView } from '#gorhom/bottom-sheet';
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
const App = () => {
// hooks
const sheetRef = useRef<BottomSheet>(null);
const [refreshing, setRefreshing] = React.useState(false);
const [listData, setListData] = React.useState([]);
const onRefresh = React.useCallback(async () => {
console.log('Refresh');
setRefreshing(true);
try {
const n = getRandomInt(10, 30);
console.log(n);
let response = await fetch('https://randomuser.me/api/?results=' + n);
let responseJson = await response.json();
setListData(responseJson.results);
setRefreshing(false);
} catch (error) {
console.error(error);
}
}, [refreshing]);
useEffect(() => {
onRefresh();
}, []);
// variables
const snapPoints = useMemo(() => ['25%', '50%', '90%'], []);
// callbacks
const handleSheetChange = useCallback((index) => {
console.log('handleSheetChange', index);
}, []);
const handleSnapPress = useCallback((index) => {
sheetRef.current?.snapTo(index);
}, []);
const handleClosePress = useCallback(() => {
sheetRef.current?.close();
}, []);
// render
const renderItem = useCallback(
(item) => (
<View key={item.login.uuid} style={styles.itemContainer}>
<Text>{item.email}</Text>
</View>
),
[]
);
return (
<View style={styles.container}>
<BottomSheet
ref={sheetRef}
index={2}
snapPoints={snapPoints}
onChange={handleSheetChange}
enableContentPanningGesture={false}
enableHandlePanningGesture={false}>
<BottomSheetScrollView
contentContainerStyle={{
backgroundColor: 'white',
//paddingTop: Platform.OS === 'ios' ? 0 : 64,
}}
contentInset={{ top: 64 }}
contentOffset={{ x: 0, y: -64 }}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
progressViewOffset={64}
colors={['#000']}
tintColor={'#000'}
/>
}>
{listData.map(renderItem)}
</BottomSheetScrollView>
</BottomSheet>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 200,
},
contentContainer: {
backgroundColor: 'white',
},
itemContainer: {
padding: 6,
margin: 6,
backgroundColor: '#eee',
},
});
export default App;

Unable to clear setInterval in react native app

I have this very simple app, where I am trying to implement countdown, Start and Stop functionality seems to be working fine but when I press Stop, the value of seconds is not updating in the view which is desired behaviour but when I observed console log, it it showing the value of sec continuously changing which I guess shows that setInterval is still running and not cleared.
Here is the accompanied code:
import React, { useState, useEffect } from "react";
import {
StyleSheet,
Text,
View,
StatusBar,
TouchableOpacity,
Dimensions,
} from "react-native";
const screen = Dimensions.get("screen");
export default function App() {
const [seconds, setSeconds] = useState(4);
const [start, setStartToggle] = useState(true);
let [fun, setFun] = useState(null);
const getRemaining = () => {
const minute = Math.floor(seconds / 60);
const second = seconds - minute * 60;
return formatTime(minute) + ":" + formatTime(second);
};
const formatTime = (time) => {
return ("0" + time).slice(-2);
};
const startTimer = () => {
setStartToggle(false);
console.log("StartTimer");
let sec = seconds;
setFun(
setInterval(() => {
console.log("akak:", sec);
if (sec <= 0) stopTimer();
setSeconds(sec--);
}, 1000)
);
};
const stopTimer = () => {
setStartToggle(true);
console.log("StopTimer");
clearInterval(fun);
setFun(null);
};
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
<Text style={styles.timerText}>{getRemaining(seconds)}</Text>
{start ? (
<TouchableOpacity onPress={startTimer} style={styles.button}>
<Text style={styles.buttonText}>Start</Text>
</TouchableOpacity>
) : (
<TouchableOpacity
onPress={stopTimer}
style={[styles.button, { borderColor: "orange" }]}
>
<Text style={styles.buttonText}>Stop</Text>
</TouchableOpacity>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#07121B",
alignItems: "center",
justifyContent: "center",
},
button: {
borderWidth: 10,
borderColor: "#89aaff",
width: screen.width / 2,
height: screen.width / 2,
borderRadius: screen.width / 2,
justifyContent: "center",
alignItems: "center",
},
buttonText: {
fontSize: 40,
color: "#89aaff",
fontWeight: "bold",
},
timerText: {
fontSize: 90,
color: "#89aaff",
fontWeight: "bold",
marginBottom: 20,
},
});
App Behaviour:
Output being shown even after Stop is hit:
Thanks to Emiel Zuurbier and lissettdm for their answers, my code is working now.
Working code:
// import { StatusBar } from "expo-status-bar";
import React, { useState, useEffect, useRef } from "react";
import {
StyleSheet,
Text,
View,
StatusBar,
TouchableOpacity,
Dimensions,
} from "react-native";
const screen = Dimensions.get("screen");
export default function App() {
const [seconds, setSeconds] = useState(11);
const funRef = useRef(null);
const [start, setStartToggle] = useState(true);
const getRemaining = () => {
const minute = Math.floor(seconds / 60);
const second = seconds - minute * 60;
return formatTime(minute) + ":" + formatTime(second);
};
const formatTime = (time) => {
return ("0" + time).slice(-2);
};
let sec = seconds;
useEffect(() => {
// let timer = null;
if (!start) {
let sec = seconds;
funRef.current = setInterval(() => {
console.log("Seconds remaining:", sec);
if (sec <= 0) {
clearInterval(funRef.current);
setStartToggle(true);
}
setSeconds(sec--);
}, 1000);
} else {
clearInterval(funRef.current);
}
}, [start]);
const startTimer = () => {
setSeconds(sec);
setStartToggle(false);
};
const stopTimer = () => {
setSeconds(sec);
setStartToggle(true);
};
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
<Text
style={
seconds ? styles.timerText : [styles.timerText, { color: "red" }]
}
>
{getRemaining(seconds)}
</Text>
{start ? (
<TouchableOpacity onPress={startTimer} style={styles.button}>
<Text style={styles.buttonText}>Start</Text>
</TouchableOpacity>
) : (
<TouchableOpacity
onPress={stopTimer}
style={[styles.button, { borderColor: "orange" }]}
>
<Text style={styles.buttonText}>Stop</Text>
</TouchableOpacity>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#07121B",
alignItems: "center",
justifyContent: "center",
},
button: {
borderWidth: 10,
borderColor: "#89aaff",
width: screen.width / 2,
height: screen.width / 2,
borderRadius: screen.width / 2,
justifyContent: "center",
alignItems: "center",
},
buttonText: {
fontSize: 40,
color: "#89aaff",
fontWeight: "bold",
},
timerText: {
fontSize: 90,
color: "#89aaff",
fontWeight: "bold",
marginBottom: 20,
},
});
Currently you use the useState hook to store the interval reference. At every re-render of the App component the fun state is reset, but will not be the exact same reference to the interval.
Instead use the useRef hook. It can create a reference to the interval which will not change during re-renders. This means that the value in the current property of the reference will always be the exact same one.
To top it off, use the useEffect hook to watch when a running state is set or unset and start and stop the timer based on that state.
import React, { useState, useRef, useEffect } from 'react'
export default function App() {
const [isRunning, setIsRunning] = useState(false);
const funRef = useRef(null);
const startTimer = () => {
if (!isRunning) {
setIsRunning(true);
}
};
const stopTimer = () {
if (isRunning && funRef.current !== null) {
setIsRunning(false);
}
};
useEffect(() => {
if (isRunning) {
funRef.current = setInterval(() => { // Save reference to interval.
// ...
}, 1000);
} else {
clearInterval(funRef.current); // Stop the interval.
}
}, [isRunning]);
// ...
}
Add interval timer inside useEffect block, and run this block every time start changes:
useEffect(()=> {
let timer = null;
if(!start) {
let sec = seconds;
timer = setInterval(() => {
console.log("akak:", sec);
if (sec <= 0) clearInterval(timer);
setSeconds(sec--);
}, 1000);
} else {
clearInterval(timer);
}
}, [start]);
const startTimer = () => {
setStartToggle(false);
};
const stopTimer = () => {
setStartToggle(true);
};
Also, I recommend you to use the exact remaining time. Javascript is a single threaded language, and this interval could be more than one second
EDIT: using the exact time.
useEffect(() => {
let timer = null;
if (!start) {
let sec = seconds;
const startTime = Date.now();
timer = setInterval(() => {
const currentTime = Date.now();
sec = sec - Math.floor((currentTime - startTime) / 1000);
if (sec < 0) {
clearInterval(timer);
setSeconds(0);
} else {
setSeconds(sec);
}
}, 1000);
}
}, [start]);

React useState starts bugging after multiple state changes

Im working on a cookie clicker type game and Im working on making features like automatic clickers / combo multipliers. I created the start of the game but when I use my auto function it begins bugging after multiple clicks of the clicker. I have linked the code and an example of the bug occurring below.
CODE
import React, { useState, useEffect } from "react";
import { StyleSheet, Text, View, Image } from "react-native";
import { cases } from "./cases";
import { motion } from "framer-motion";
const io = require("socket.io-client");
const SocketEndpoint = "http://localhost:3000/";
const App = () => {
const [cash, setCash] = useState(0);
const [clicks, setClicks] = useState(0);
const [bot, setBot] = useState(0);
const [open, setOpen] = useState(false);
const [prize, setPrize] = useState(null);
const [connected, setConnected] = useState(false);
const [upgrades, setUpgrades] = useState({auto: false, multi: false})
const [multi, setMulti] = useState(1);
let applied = false;
useEffect(() => {[![enter image description here][1]][1]
const socket = io(SocketEndpoint, {
transports: ["websocket"],
});
socket.on("connect", () => {
setConnected(true)
});
}, [])
useEffect(() => {
if(!applied) {
autoUpgrade();
applied = true;
}
}, [bot, upgrades])
const autoUpgrade = () => {
if (!upgrades.auto) {
return;
}
const inter = setInterval(() => setBot(bot + 1), 1000);
return () => clearInterval(inter);
}
const collectBotIncome = () => {
setCash(cash + bot);
setBot(0);
}
const addMulti = () => {
if (!upgrades.multi) {
return;
}
setMulti(2)
}
const increasePoint = () => {
setClicks(clicks + (1 * multi))
}
const purchaseCase = () => {
setClicks(clicks - 10)
}
const purchaseUpgrade = (upgrade) => {
alert('purchased')
upgrade === 'auto' ? setUpgrades({...upgrades, auto: true}) : null;
upgrade === 'multi' ? setUpgrades({...upgrades, multi: true}) : null;
}
const between = (x, min, max) => {
return x >= min && x <= max;
}
const openCase = () => {
setOpen(true);
purchaseCase();
const ticket = Math.floor(Math.random() * 10000);
console.log('[ROUND] Ticket:' + ticket)
var found = false;
var i;
for (i = 0; i < cases.types[0].items.length; i++) {
var item = cases.types[0].items[i];
if(between(ticket, item.ticketStart, item.ticketEnd)) {
setPrize(item.name)
found = true;
}
}
}
return (
<View style={styles.container}>
<View style={styles.shopMenu}>
<Text style={{color: 'white'}}>Shop</Text>
<Text onClick={() => purchaseUpgrade('auto')} style={{color: 'white'}}>Auto Click Level 1</Text>
<Text onClick={() => purchaseUpgrade('multi')} style={{color: 'white'}}>Click Multiplier Level 1</Text>
</View>
<View style={styles.shopMenu}>
<Text style={{color: 'white'}}>Inventory</Text>
{upgrades.auto
?
<>
<Text style={{color: 'white'}}>{bot}</Text>
<Text onClick={collectBotIncome} style={{color: 'white'}}>Collect Bot Income</Text>
</>
: null
}
</View>
<motion.img
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={increasePoint}
src={require('./src/assets/case.png')} alt="case"
/>
<Text style={{color: 'white'}}>clicks: {clicks ? clicks : "0"}</Text>
<Text style={{color: 'white'}}>connected: {connected ? "true" : "false"}</Text>
{open
? <Text style={{color: 'white'}}>{prize ? prize : 'Failed to get Prize'}</Text>
: <Image
style={clicks >= 10 ? styles.available : styles.unavailable}
source={require('./src/assets/case.png')} alt="case"
onClick={openCase}
/>
}
<Text style={{color: 'white'}}>{cases.types[0].name}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "black",
alignItems: "center",
justifyContent: "center",
},
available: {
display: 'flex',
width: 150,
height: 100,
filter: 'none',
transition: '200ms ease',
marginTop: 50
},
unavailable: {
display: "flex",
width: 150,
height: 100,
filter: 'grayscale(1)',
transition: '200ms ease',
marginTop: 50
},
shopMenu: {
display: "flex",
flexDirection: "column"
}
});
export default App;
PREVIEW

Basic react native question covert Class to Function

It might sound silly but I am just learning here
I am trying to convert a component to a function-based component. I did everything right but I am stuck on something very silly
I have this code for Discover
export default class Discover extends React.PureComponent {
state = {
items: [],
};
cellRefs: {};
constructor(props) {
super(props);
this.cellRefs = {};
}
what is the correct way to convert cellRefs to work with the function I have? I tried everything when I do this in my class file it is fine it gives me an object with the things I need.
const cell = this.cellRefs[item.key];
However,
const cell = cellRefs[item.key];
is just giving undefined
Full code for the converted component
import React, { useState, useEffect, useRef, Children } from 'react';
import {
StyleSheet,
Text,
View,
FlatList,
Dimensions,
TouchableOpacity,
Image,
} from 'react-native';
import { Video } from 'expo-av';
const { height, width } = Dimensions.get('window');
const cellHeight = height * 0.6;
const cellWidth = width;
const viewabilityConfig = {
itemVisiblePercentThreshold: 80,
};
class Item extends React.PureComponent {
video: any;
componentWillUnmount() {
if (this.video) {
this.video.unloadAsync();
}
}
async play() {
const status = await this.video.getStatusAsync();
if (status.isPlaying) {
return;
}
return this.video.playAsync();
}
pause() {
if (this.video) {
this.video.pauseAsync();
}
}
render() {
const { id, poster, url } = this.props;
const uri = url + '?bust=' + id;
return (
<View style={styles.cell}>
<Image
source={{
uri: poster,
cache: 'force-cache',
}}
style={[styles.full, styles.poster]}
/>
<Video
ref={ref => {
this.video = ref;
}}
source={{ uri }}
shouldPlay={false}
isMuted
isLooping
resizeMode="cover"
style={styles.full}
/>
<View style={styles.overlay}>
<Text style={styles.overlayText}>Item no. {id}</Text>
<Text style={styles.overlayText}>Overlay text here</Text>
</View>
</View>
);
}
}
interface FeedListProps {
}
export const FeedList: React.FC<FeedListProps> = (props) => {
const initialItems = [
{
id: 1,
url: 'https://s3.eu-west-2.amazonaws.com/jensun-uploads/shout/IMG_1110.m4v',
poster:
'https://s3.eu-west-2.amazonaws.com/jensun-uploads/shout/norwaysailing.jpg',
},
{
id: 2,
url:
'https://s3.eu-west-2.amazonaws.com/jensun-uploads/shout/croatia10s.mp4',
poster:
'https://s3.eu-west-2.amazonaws.com/jensun-uploads/shout/croatia10s.jpg',
},
];
const [items, setItems] = useState([]);
//this.cellRefs = {};
//let cellRefs: {};
//cellRefs= {};
const cellRefs = React.useRef({})
const viewConfigRef = React.useRef({ itemVisiblePercentThreshold: 80 })
useEffect(() => {
loadItems();
setTimeout(loadItems, 1000);
setTimeout(loadItems, 1100);
setTimeout(loadItems, 1200);
setTimeout(loadItems, 1300);
}, []);
const _onViewableItemsChanged = React.useRef((props)=>{
const changed = props.changed;
changed.forEach(item => {
const cell = cellRefs[item.key];
console.log("CALLING IF"+ cell + " " + item.key)
if (cell) {
if (item.isViewable) {
console.log("PLAY OS CALLED")
cell.play();
} else {
console.log("Pause is played")
cell.pause();
}
}
});
});
function loadItems(){
const start = items.length;
const newItems = initialItems.map((item, i) => ({
...item,
id: start + i,
}));
const Litems = [...items, ...newItems];
setItems( Litems );
};
function _renderItem({ item }){
return (
<Item
ref={cellRefs[item.id]}
{...item}
/>
);
};
return (
<View style={styles.container}>
<FlatList
style={{ flex: 1 }}
data={items}
renderItem={_renderItem}
keyExtractor={item => item.id.toString()}
onViewableItemsChanged={_onViewableItemsChanged.current}
initialNumToRender={3}
maxToRenderPerBatch={3}
windowSize={5}
getItemLayout={(_data, index) => ({
length: cellHeight,
offset: cellHeight * index,
index,
})}
viewabilityConfig={viewabilityConfig}
removeClippedSubviews={true}
ListFooterComponent={
<TouchableOpacity onPress={loadItems}>
<Text style={{ padding: 30 }}>Load more</Text>
</TouchableOpacity>
}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
cell: {
width: cellWidth - 20,
height: cellHeight - 20,
backgroundColor: '#eee',
borderRadius: 20,
overflow: 'hidden',
margin: 10,
},
overlay: {
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
backgroundColor: 'rgba(0,0,0,0.4)',
padding: 40,
},
full: {
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
},
poster: {
resizeMode: 'cover',
},
overlayText: {
color: '#fff',
},
});
Should be enough to use a functional component and use useRef([]) initialized as an array
Working example:
https://snack.expo.io/2_xrzF!LZ
It would be best if you could inline your source code into stack overflow, than keeping it in a separate link. However I took a look at the component, and I think the issue is with how you are using props.
On line 91,
export const FeedList: React.FC<FeedListProps> = ({}) => {
So here, you need to get the props as an argument. Earlier with the class component it was available at this.props. However here you need to pass it as below,
export const FeedList: React.FC<FeedListProps> = (props) => {
Or you may destructure the props as below,
export const FeedList: React.FC<FeedListProps> = ({changed}) => {
You will also need to modify the interface on line 87 to reflect the type.

Categories

Resources