Restart Countdown timer in react native - javascript

working on a countdown timer, I want to be able to restart the countdown onpress of the button, however, I don't think the button is functional as I get no feedback. can someone please point me in the right direction. below is a trimmed down sample code of what I am trying to achieve.
export default class Example extends Component {
constructor(props) {
super(props);
this.state = {
timer: 10,
timesup: false,
timing: true,
showWelcome: true,
};
}
componentDidMount() {
this.clockCall = setInterval(() => {
this.decrementClock();
}, 1000);
}
startTimer = () => {
this.setState({
timing: true,
timer: 30,
showWelcome: false
})
}
decrementClock = () => {
this.setState((prevstate) => ({
timer: prevstate.timer - 1
}), () => {
if (this.state.timer === 0) {
clearInterval(this.clockCall)
this.setState({
timesup: true,
timing: false,
showWelcome: false,
})
}
})
}
componentWillUnmount() {
clearInterval(this.clockCall);
}
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
{this.state.timesup && (
<Text style={{fontSize: 18, color: '#000'}}>
Time up
</Text>)}
{this.state.timing && (
<Text style={{fontSize: 18, color: '#000'}}>
{this.state.timer}
</Text>)}
{this.state.showWelcome && (
<Text style={{ fontSize: 20 }}>Welcome</Text>
)}
<Button Onpress={this.startTimer.bind(this)} title='play' />
</View>
)
}
}

I believe you're looking for onPress, not Onpress. Also, if you are using:
startTimer = () => ...
Using:
this.startTimer.bind
Has no effect, since the method is already bound to this by the arrow function. You can then simply use:
onPress={this.startTimer}

Related

FlatList component is not showing result

I am working on a task in which I have to implement infinite scroll paging of 600 dummy records to cards I have coded on FlatList render. So far I have implemented logic using FlatList but not successfully. Help is required where I am doing wrong thus not giving result. Here is the component code:
import React, { Component } from "react";
import { StyleSheet, ActivityIndicator, FlatList, View } from "react-native";
import { Card } from "react-native-elements";
class InfiniteScroll extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
data: [],
dummy: [],
fetchingStatus: false,
setOnLoad: false,
};
this.page = 0;
let onEndReached = false;
}
componentDidMount() {
var toput = [];
for (let i = 0; i < 598; i++) {
toput.push({
name: "KHAN MARKET",
order_no: "ORDER # DBZ-876",
status: "Order Completed",
price: "Total: $14.00",
date: "Dec 19, 2019 2:32 PM",
});
}
this.setState(
{
dummy: toput,
},
() => {
console.log(this.state.dummy);
}
);
this.apiCall();
}
apiCall = () => {
var that = this;
var old = that.page;
that.page = that.page + 10;
console.log(" *********** call " + this.page);
that.setState({ fetchingStatus: true });
that.setState({
data: [...this.state.data, ...this.state.dummy.slice(old, that.page)],
isLoading: false,
fetchingStatus: false,
setOnLoad: true,
});
};
BottomView = () => {
return (
<View>
{this.state.fetchingStatus ? (
<ActivityIndicator
size="large"
color="#F44336"
style={{ marginLeft: 6 }}
/>
) : null}
</View>
);
};
ItemSeparator = () => {
return (
<View
style={{
height: 0.5,
width: "100%",
backgroundColor: "#607D8B",
}}
/>
);
};
render() {
return (
<View>
{this.state.isLoading ? (
<ActivityIndicator size="large" />
) : (
<FlatList
style={{ width: "100%" }}
keyExtractor={(item, index) => index}
data={this.state.data}
ItemSeparatorComponent={this.ItemSeparator}
onScrollEndDrag={() => console.log(" *********end")}
onScrollBeginDrag={() => console.log(" *******start")}
initialNumToRender={8}
maxToRenderPerBatch={2}
onEndReachedThreshold={0.1}
onMomentumScrollBegin={() => {
this.onEndReached = false;
}}
onEndReached={() => {
if (!this.onEndReached) {
this.apiCall(); // on end reached
this.onEndReached = true;
}
}}
renderItem={({ item, index }) => (
<View>
<Card>
<Text
style={{ justifyContent: "flex-start", fontWeight: "bold" }}
>
{item.name}
</Text>
<View style={styles.textview}>
<Text style={styles.orderno}>{item.order_no}</Text>
<Text style={styles.orderstatus}>{item.status}</Text>
</View>
<Text style={styles.amount}>{item.price}</Text>
<View style={styles.textview}>
<Text style={styles.orderno}>{item.date}</Text>
</View>
</Card>
</View>
)}
ListFooterComponent={this.BottomView}
/>
)}
</View>
);
}
}
const styles = StyleSheet.create({
textview: {
marginTop: 5,
flexDirection: "row",
justifyContent: "space-between",
},
orderno: {
flexDirection: "row",
justifyContent: "flex-start",
},
orderstatus: {
flexDirection: "row",
justifyContent: "flex-end",
color: "green",
},
ordercomplete: {
flexDirection: "row",
justifyContent: "flex-end",
color: "red",
},
amount: {
justifyContent: "flex-start",
marginTop: 5,
},
});
export default InfiniteScroll;
I will be very thankful if you can provide solution. Thanks
The thing here is that you are seting your dummy data and calling the apiCall next, the dummy data is not updated yet when you call the apiCall, ande the dummy data is shown as an empty array on the apiCall, you just ned to force some await before calling the function, just change from this:
this.setState(
{
dummy: toput,
},
() => {
console.log(this.state.dummy);
}
);
this.apiCall();
to this:
this.setState(
{
dummy: toput
},
() => {
this.apiCall();
}
);
Another thing I notice is that you are using some bad pratices like setting valuse direct on the class instead of using state and some messing with the scope (that = this), I've made some updates/suggestions in you code that may help with readability:
apiCall = () => {
const newPage = this.state.page + 10;
this.setState({
data: [
...this.state.data,
...this.state.dummy.slice(this.state.page, newPage)
],
isLoading: false,
fetchingStatus: false,
setOnLoad: true,
page: newPage
});
};
I've created a fully working example so you can see some more updates I've made in the code, that may help you as well:
https://codesandbox.io/s/stackoverflow-67166780-321ku?file=/src/App.js

Like button on React Native? I can't make it dynamically update

class PostButtons extends Component {
constructor() {
super();
this.state = {
liked: true,
};
}
likeToggled() {
this.setState({
liked: !this.state.liked,
});
}
render() {
const heartIconColor = () => {
if (this.state.liked === true) {
return "red";
} else if (this.state.liked === false) {
return "black";
}
};
<View>
<TouchableOpacity
onPress={() => {
this.likeToggled;
}}
>
<Image
style={{
height: 37,
width: 37,
marginVertical: 395,
marginHorizontal: 10,
justifyContent: "flex-start",
position: "absolute",
tintColor: heartIconColor(),
}}
source={require("../Img/Heart3.png")}
></Image>
</TouchableOpacity>
</View>
I need it so when someone clicks the like button (initial state is black) it turns red then when they click it again it goes back to black. Currently i need to change this.state.liked to true and false to change the colours. I am not that advanced so an answer thats not verbose would be greatly appreciated. Thanks
you weren't calling your likeToggled function. you were just accessing its definition which doesn't run the code inside your likeToggled function calling it like likeToggled() does
<TouchableOpacity onPress={() => { this.likeToggled();}} >

Why is socket.on() from a previous screen component executed?

I have two react components. the first Lobby uses react-native-navigation to push Gameroom to the stack. It passes props such as the socket object and other data to the Gameroom component
when the back button of the navigation bar is pressed inside Gameroom, a socket.io leave event is emitted, and I have verified it is heard by the server, so the socket passed through props works. the server then emits an event left back to the socket.io room (Gameroom component).
the left event listener, if placed inside Gameroom's componentDidMount() does not execute. However, if the same socket.io event listener is placed in Lobby component (previous screen) componentDidMount() the event is heard
I've tried adding the event listener to multiple componentDidMount functions, I also thought about using the Context API, but I'm not working with nested components. I'm passing the socket object in react-native-navigation's {passProps} from screen to screen
Lobby:
imports ...
const socket = io("http://192.xxx.xxx.xx:3000");
export default class Lobby extends React.Component {
static options(passProps) {
return {
topBar: {
background: {
color: "transparent"
},
drawBehind: true,
visible: true,
animate: true,
leftButtons: [
{
id: "leave",
icon: require("../assets/img/Chevron.png")
}
]
}
};
}
constructor(props) {
super(props);
this.state = {
username: "Initializing...",
queue: []
};
}
componentDidMount() {
Navigation.events().bindComponent(this);
socket.emit("lobbyEntry");
socket.on("lobbyEntry", entry => {
this.setState({ queue: entry.lobby, username: socket.id });
});
socket.on("userJoined", lobby => {
this.setState({ queue: lobby });
});
// socket.on("left", () => {
// alert("Opponent Left...Oh well");
// Navigation.pop(this.props.componentId);
// });
}
navigationButtonPressed({ buttonId }) {
switch (buttonId) {
case "leave":
socket.emit("leave");
Navigation.popToRoot(this.props.componentId);
break;
}
}
createMatch = () => {
if (this.state.username != "Initializing...") {
socket.emit("findMatch");
socket.on("alreadyCreated", () => {
alert("You already created a match!");
});
socket.on("listUsers", lobby => {
this.setState({ queue: lobby });
});
socket.on("matchFound", data => {
Navigation.push(this.props.componentId, {
component: {
name: "Gameroom",
passProps: {
room: data.id,
socket: socket,
firstMove: data.firstMove,
p1: data.p1,
p2: data.p2
}
}
});
});
} else {
alert("Wait for Username to be initialized...");
}
};
render() {
const bg = getBackground();
return (
<ImageBackground source={bg} style={{ height: "100%", width: "100%" }}>
<View style={styles.title_container}>
<Text style={styles.title_sm}>Matchmaking Lobby</Text>
</View>
<View style={styles.alt_text_container}>
<Text style={styles.alt_text_md}>Username:</Text>
<Text style={styles.alt_text_md}>{this.state.username}</Text>
</View>
<View
style={{
flexDirection: "column",
justifyContent: "center",
alignItems: "center"
}}
>
<XplatformButton onPress={this.createMatch} text={"Create a Match"} />
</View>
<View style={styles.alt_text_container}>
<Text style={styles.alt_text_sm}>Players actively searching...</Text>
<FlatList
style={styles.alt_text_container}
data={this.state.queue}
renderItem={({ item, index }) => (
<Text style={styles.alt_text_md} key={index}>
{item}
</Text>
)}
/>
</View>
</ImageBackground>
);
}
}
Gameroom:
import ...
export default class Gameroom extends React.Component {
static options(passProps) {
return {
topBar: {
title: {
fontFamily: "BungeeInline-Regular",
fontSize: styles.$navbarFont,
text: "Gameroom - " + passProps.room,
color: "#333"
},
background: {
color: "transparent"
},
drawBehind: true,
visible: true,
animate: true,
leftButtons: [
{
id: "leave",
icon: require("../assets/img/Chevron.png")
}
]
}
};
}
constructor(props) {
super(props);
Navigation.events().bindComponent(this);
}
navigationButtonPressed({ buttonId }) {
switch (buttonId) {
case "leave":
this.props.socket.emit("leave");
Navigation.pop(this.props.componentId);
break;
}
}
componentDidMount() {
// socket.on("left", () => {
// alert("Opponent Left...Oh well");
// Navigation.pop(this.props.componentId);
// });
}
render() {
const bg = getBackground();
return this.props.p2 != null ? (
<Gameboard
room={this.props.room}
you={
this.props.socket.id == this.props.p1.username
? this.props.p1.marker
: this.props.p2.marker
}
opponent={
this.props.socket.id != this.props.p1.username
? this.props.p2.marker
: this.props.p1.marker
}
move={this.props.firstMove}
socket={this.props.socket}
/>
) : (
<ImageBackground style={styles.container} source={bg}>
<View style={{ marginTop: 75 }}>
<Text style={styles.alt_text_md}>
Waiting for Opponent to Join...
</Text>
</View>
</ImageBackground>
);
}
}
I expect the event listener to execute from the current screen's componentDidMount() function, but it only executes if it's inside the previous screen's componentDidMount()
When you create a component,
the constructor -> componentWillMount -> render -> componentDidMount is
followed.
In your Lobby class, the event listener is run because it is in ComponentDidmont.
However, the event listener of the Gameroom class is inside the constructor. If executed within the constructor, the event cannot be heard because it is not yet rendered.
Event listeners are called when they appear on the screen
Usage
componentDidMount() {
this.navigationEventListener = Navigation.events().bindComponent(this);
}
componentWillUnmount() {
// Not mandatory
if (this.navigationEventListener) {
this.navigationEventListener.remove();
}
}

PanResponder not working properly on Android when Animated.View has an Animated.ScrollView as a child

I have a an issue on Android with my pangesture.
I have an Animated.ScrollView inside my Animated.View. Whenever I drag my Animated.View slowly, it works perfectly but whenever I flick really quick, the Animated.View stops after a couple of pixels.
The bug doesn't happen when I disable the scroll on the Animated.ScrollView. My guess is that the Animated.ScrollView takes over when swiping too fast.
Anyone has an idea why this happens? This does not happen on iOS.
Detail View:
class DetailScreen extends PureComponent<Props> {
state = {
closeToTop: true,
closeToBottom: false
};
scrollY = new Animated.Value(0);
scrollView?: ScrollView;
handleScroll = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
const closeToTop = this.isCloseToTop(e);
if (closeToTop !== this.state.closeToTop) {
this.setState({ closeToTop });
}
const closeToBottom = this.isCloseToBottom(e);
if (closeToBottom !== this.state.closeToBottom) {
this.setState({ closeToBottom });
}
};
isCloseToTop = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
const { contentOffset } = e.nativeEvent;
return contentOffset.y === 0;
};
isCloseToBottom = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
const { layoutMeasurement, contentOffset, contentSize } = e.nativeEvent;
return layoutMeasurement.height + contentOffset.y >= contentSize.height - 20;
};
renderContent = () => {
return (
<Animated.ScrollView
contentContainerStyle={{ flexGrow: 1 }}
scrollEventThrottle={16}
bounces={false}
// scrollEnabled={false}
overScrollMode={"never"}
onScroll={Animated.event(
[
{
nativeEvent: {
contentOffset: {
y: this.scrollY
}
}
}
],
{
useNativeDriver: true,
listener: this.handleScroll
}
)}
ref={(ref: any) => (this.scrollView = ref && ref.getNode())}>
<TouchableWithoutFeedback>
<View style={{flex: 1}}>
<View style={{...styles.container, height: 200, backgroundColor: "red"}}>
<Text>red</Text>
</View>
<View style={{ ...styles.container, height: 200, backgroundColor: "yellow" }}>
<Text>yellow</Text>
</View>
<View style={{ ...styles.container, height: 200, backgroundColor: "green" }}>
<Text>green</Text>
</View>
<View style={{ ...styles.container, height: 200, backgroundColor: "blue" }}>
<Text>blue</Text>
</View>
<View style={{ ...styles.container, height: 200, backgroundColor: "red" }}>
<Text>red</Text>
</View>
<View style={{ ...styles.container, height: 200, backgroundColor: "yellow" }}>
<Text>yellow</Text>
</View>
</View>
</TouchableWithoutFeedback>
</Animated.ScrollView>
);
};
render() {
return (
<SwipeableModal
onClose={this.props.navigation.pop}
nearTop={this.state.closeToTop}
nearBottom={this.state.closeToBottom}>
{this.renderContent()}
</SwipeableModal>
);
}
}
Swipeable Modal
const { height: screenHeight } = Dimensions.get("window");
const TIMING_CONFIG = { duration: 300, easing: Easing.inOut(Easing.ease) };
class SwipeableModal extends PureComponent<Props, State> {
panResponder: PanResponderInstance;
constructor(props: Props) {
super(props);
this.state = {
pan: new Animated.ValueXY({ x: 0, y: 0 }),
isDragging: false
};
this.panResponder = PanResponder.create({
// Ask to be the responder:
onStartShouldSetPanResponder: () => false,
onStartShouldSetPanResponderCapture: () => false,
onMoveShouldSetPanResponderCapture: () => false,
onPanResponderTerminationRequest: () => false,
onMoveShouldSetPanResponder: (evt, gestureState) => {
if (this.state.isDragging) {
return true;
}
// moving finger from top to bottom
if (gestureState.vy > 0 && this.props.nearTop) {
this.setState({ isDragging: true });
return true;
}
// moving finger from bottom to top
if (gestureState.vy < 0 && this.props.nearBottom) {
this.setState({ isDragging: true });
return true;
}
return false;
},
onPanResponderMove: (evt, gestureState) => {
this.state.pan.setValue({ x: 0, y: gestureState.dy });
},
onPanResponderRelease: (evt, gestureState) => {
this.setState({ isDragging: false });
if (gestureState.vy <= -0.7 || gestureState.dy <= -300) {
// move from bottom to top
Animated.timing(this.state.pan, {
toValue: { x: 0, y: -screenHeight },
...TIMING_CONFIG
}).start(this.closeModal);
} else if (gestureState.vy >= 0.5 || gestureState.dy >= 300) {
// move from top to
Animated.timing(this.state.pan, {
toValue: { x: 0, y: screenHeight },
...TIMING_CONFIG
}).start(this.closeModal);
} else {
Animated.spring(this.state.pan, {
toValue: 0
}).start();
}
}
});
}
closeModal = () => {
this.props.onClose();
};
handleGetStyle() {
return [
style.container,
{
transform: [...this.state.pan.getTranslateTransform()]
}
];
}
render() {
return (
<Animated.View style={this.handleGetStyle()} {...this.panResponder.panHandlers}>
{this.props.children}
</Animated.View>
);
}
}
Video example
Github project that reproduces the bug
For those wondering, I was able to fix this issue by reimplementing the swipe-able modal with react-native-gesture-handler.
If anyone needs help, let me know.

react native setState in setInterval(), then setInterval executes error

Here is my code
componentDidMount() {
let that = this;
setInterval(() => {
that.setState({number: 1});
}, 2000);
}
I have writen 'let that = this;', but it's also error. In 2 seconds it executes more than once.
Why aren't you using this itself in the setInterval? You have used the fat arrow function, so you still can use this inside.
Here's a sample code:
constructor (props) {
super(props)
this.state = {
number: 0
}
}
componentDidMount(){
setInterval(() => {
this.setState({number: parseInt(this.state.number, 10) + 1 });
}, 2000);
}
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center',}}>
<Text>
{this.state.number }
</Text>
</View>
);
}
Expo Demo

Categories

Resources