I tried to create a long press button to keep counting, however the interval wont stop when I release the onPressOut
const App = () => {
const [count, setCount] = useState(0);
let timer;
const onButtonPressIn = () => {
timer = setInterval(() => {
console.log("in");
setCount(count => count + 1);
}, 1000);
}
const onButtonPressOut = () =>{
console.log("out");
clearInterval(timer);
}
return(
<View>
<TouchableOpacity style={styles.longbtn}
activeOpacity={0.9}
onPressIn={()=> onButtonPressIn()}
onPressOut={()=> onButtonPressOut()}
>
<Text style={styles.longbtntt}>LONG PRESS TO START - {count}</Text>
</TouchableOpacity>
</View>
)
}
The method used to handle the timer is not the exact one for the sort of use case you are trying to implement it for. You have to use clearTimeout instead of clearInterval when you're dismissing the Timer . Solution is as below, just modify it to use hooks.
import React from 'react';
import {
SafeAreaView,
View,
Dimensions,
TouchableOpacity,
Text
} from 'react-native';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 0
};
this.timer = null;
}
addOne = () => {
this.setState({counter: this.state.counter+1});
this.timer = setTimeout(this.addOne, 200);
}
stopTimer = () => {
clearTimeout(this.timer);
}
render() {
const screenHeight = Math.round(Dimensions.get('window').height);
return (
<SafeAreaView>
<View style={{height: screenHeight}}>
<TouchableOpacity
style={{backgroundColor: "#FFC0CB"}}
onPressIn={this.addOne}
onPressOut={this.stopTimer}
>
<Text>Press to start</Text>
</TouchableOpacity>
<Text>{this.state.counter}</Text>
</View>
</SafeAreaView>
);
}
}
The timer changes value when you assign a new interval to it, but the function onButtonPressOut remembers the value that timer had when the function was created. You can use useCallback here, like so:
const onButtonPressOut = useCallback(() => { //same thing }, [timer]);
This will update the function whenever timer changes and clear the appropriate interval.
Related
I am using the react-native-vision-camera package for recording the video.
I want to auto-start recording video as soon as the camera opens. and stop recording when I press the stop button and start recording another video as soon as I stop the previous video.
This is my code.
import React from 'react'
import { useState, useEffect, useMemo } from 'react'
import { StyleSheet, View, TouchableOpacity, Text } from 'react-native'
import { Camera, sortDevices } from 'react-native-vision-camera'
const App = () => {
const camera = React.useRef(null)
const [devices, setDevices] = useState([])
const device = useMemo(() => devices.find((d) => d.position === 'back'), [devices])
const [permissons, setPermissons] = useState(false)
useEffect(() => {
loadDevices()
getPermissons()
}, [])
const getPermissons = async () => {
const cameraPermission = await Camera.getCameraPermissionStatus()
const microphonePermission = await Camera.getMicrophonePermissionStatus()
if (microphonePermission === 'authorized' && cameraPermission === 'authorized') {
setPermissons(true)
}
}
const loadDevices = async () => {
try {
const availableCameraDevices = await Camera.getAvailableCameraDevices()
const sortedDevices = availableCameraDevices.sort(sortDevices)
setDevices(sortedDevices)
} catch (e) {
console.error('Failed to get available devices!', e)
}
}
async function StartRecodingHandler() {
camera.current.startRecording({
flash: 'off',
onRecordingFinished: (video) => console.log(video, 'videodata'),
onRecordingError: (error) => console.error(error, 'videoerror'),
})
}
async function stopRecodingHandler() {
await camera.current.stopRecording()
}
if (device == null) {
return null
}
return (
<View>
<View style={{ height: 600 }}>
<Camera
ref={camera}
style={StyleSheet.absoluteFill}
device={device}
isActive={true}
video={true}
audio={true}
setIsPressingButton={true}
/>
</View>
<View>
<TouchableOpacity onPress={() => { stopRecodingHandler() }}>
<Text style={{ fontSize: 35 }}>STOP Recoding</Text>
</TouchableOpacity>
</View>
</View>
)
}
export default App
So, Please can Someone help me with this problem.
NetInfo is used to check the connection & theres a checkNetwork function called in onPress of TouchableOpacity. When the button is clicked once, the checkNetwork
function is called automatically multiple times without tapping the button when the network connection is lost and vice versa.
Please have a look at the code here:
Please have a look at the video
export default class App extends React.Component {
checkNetwork = () => {
NetInfo.addEventListener((state) => {
if (state.isConnected) {
alert('isConnected');
} else {
alert('not connected');
}
});
};
render() {
return (
<View style={styles.container}>
<TouchableOpacity
activeOpacity={0.8}
onPress={()=> this.checkNetwork()}>
<Text>Check here</Text>
</TouchableOpacity>
</View>
);
}
}
You should not declare event listener inside of the click itself,
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {alert: ''};
}
componentDidMount() {
NetInfo.addEventListener((state) => this.setState({ alert: state.isConnected ? 'isConnected' : 'not connected'})
}
checkNetwork = () => alert(this.state.alert)
render() {
return (
<View style={styles.container}>
<TouchableOpacity
activeOpacity={0.8}
onPress={()=> this.checkNetwork()}>
<Text>Check here</Text>
</TouchableOpacity>
</View>
);
}
}
though in your case event listener isn't exactly the fit logic for UI behavior of pressing button, so I think you might wanna use useNetInfo
import {useNetInfo} from "#react-native-community/netinfo";
class App extends React.Component {
checkNetwork = () => {
if (this.props.netInfo.isConnected) {
alert('isConnected');
} else {
alert('not connected');
}
});
};
...rest render...
}
export default () => <App netInfo={useNetInfo()} />
I'm struggling in using setInterval on react native with hooks;
I've seen some materials about it online and I'm using this custom hook which uses the state counter to display the current element from the array, overtime the counter is increased but it goes blank after it's done with the life cycle of setInterval;
How can I set it to leave it at the latest value or resetting to the first once it's done?
Also the reset button bugs sometimes, it tries to reset, but then come back stopped at the previous position, Am I doing something wrong?
My code so far:
const SensorsDetail = ({ evaluation }) => {
const [ state ] = useState(evaluation);
const [count, setCount] = useState(0)
const [running, setRunning] = useState(false)
const cb = useRef()
const id = useRef()
const start = () => setRunning(true)
const pause = () => setRunning(false)
const reset = () => {
setRunning(false)
setCount(0)
}
function callback () {
setCount(count + 1)
}
// Save the current callback to add right number to the count, every render
useEffect(() => {
cb.current = callback
})
useEffect(() => {
// This function will call the cb.current, that was load in the effect before. and will always refer to the correct callback function with the current count value.
function tick() {
cb.current()
}
if (running && !id.current) {
id.current = setInterval(tick, 250)
}
if (!running && id.current) {
clearInterval(id.current)
id.current = null
}
return () => id.current && clearInterval(id.current)
}, [running])
return(
<View style={styles.view}>
<Card>
<Text style={styles.text}>{state.dados_sensor_1[count]}</Text>
</Card>
<Card>
<Text style={styles.text}>{state.dados_sensor_2[count]}</Text>
</Card>
<Card>
<Text style={styles.text}>{state.dados_sensor_3[count]}</Text>
</Card>
<Card>
<Text style={styles.text}>{state.dados_sensor_4[count]}</Text>
</Card>
<TouchableOpacity onPress={start} style={styles.buttonStyle}>
<Text style={styles.textStyle2}>
Start
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={pause} style={styles.buttonStyle}>
<Text style={styles.textStyle2}>
Pause
</Text>
</TouchableOpacity>
<TouchableOpacity onPress={reset} style={styles.buttonStyle}>
<Text style={styles.textStyle2}>
Reset
</Text>
</TouchableOpacity>
</View>
);
};
This is silly but after studying more about it, I saw that actually what was happening was that the counter was moving past the array size, that's why it was showing blank values;
I just add a limit to value the counter could raise and it's working just fine;
The reset though is still bugging sometimes...
this is the code for the custom hook:
import { useState, useEffect, useRef } from 'react';
export default (initialCount, finalCount, autoStart) => {
const [count, setCount] = useState(initialCount)
const [running, setRunning] = useState(autoStart)
const cb = useRef()
const id = useRef()
const start = () => {
if (count < finalCount){
setRunning(true)
}
}
const pause = () => setRunning(false)
const reset = () => {
setRunning(false);
setCount(initialCount)
}
function callback () {
setCount(count + 1)
if (count == finalCount){
setRunning(false)
}
}
useEffect(() => {
cb.current = callback
})
useEffect(() => {
function tick() {
cb.current()
}
if (running && !id.current) {
id.current = setInterval(tick, 250)
}
if (!running && id.current) {
clearInterval(id.current)
id.current = null
}
return () => id.current && clearInterval(id.current)
}, [running])
return {
count,
start,
pause,
reset
}
};
I have a Main class which I show an array to user, then in detail page user can edit each element which I'm passing using react navigation parameter. I want to edit my array in the detail class and save it using async storage.
//Main.jsimport React from 'react';
import {
StyleSheet ,
Text,
View,
TextInput,
ScrollView,
TouchableOpacity,
KeyboardAvoidingView,
AsyncStorage
} from 'react-native'
import Note from './Note'
import detail from './Details'
import { createStackNavigator, createAppContainer } from "react-navigation";
export default class Main extends React.Component {
static navigationOptions = {
title: 'To do list',
headerStyle: {
backgroundColor: '#f4511e',
},
};
constructor(props){
super(props);
this.state = {
noteArray: [],
noteText: '',
dueDate: ''
};
}
async saveUserTasks(value) {
try {
await AsyncStorage.setItem('#MySuperStore:userTask',JSON.stringify(value));
} catch (error) {
console.log("Error saving data" + error);
}
}
getUserTasks = async() =>{
try {
const value = await AsyncStorage.getItem('#MySuperStore:userTask');
if (value !== null){
this.setState({ noteArray: JSON.parse(value)});
}
} catch (error) {
console.log("Error retrieving data" + error);
}
}
render() {
this.getUserTasks()
let notes = this.state.noteArray.map((val,key) => {
return <Note key={key} keyval={key} val={val}
deleteMethod={ () => this.deleteNote(key)}
goToDetailPage= {() => this.goToNoteDetail(key)}
/>
});
const { navigation } = this.props;
return(
<KeyboardAvoidingView behavior='padding' style={styles.keyboard}>
<View style={styles.container}>
<ScrollView style={styles.scrollContainer}>
{notes}
</ScrollView>
<View style={styles.footer}>
<TextInput
onChangeText={(noteText) => this.setState({noteText})}
style={styles.textInput}
placeholder='What is your next Task?'
placeholderTextColor='white'
underlineColorAndroid = 'transparent'
>
</TextInput>
</View>
<TouchableOpacity onPress={this.addNote.bind(this)} style={styles.addButton}>
<Text style={styles.addButtonText}> + </Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
}
addNote(){
if (this.state.noteText){
var d = new Date();
this.state.noteArray.push({
'creationDate': d.getFullYear() + "/" + (d.getMonth()+1) + "/" + d.getDay(), 'taskName': this.state.noteText,'dueDate':'YYYY/MM/DD'
});
this.setState({noteArray:this.state.noteArray})
this.setState({noteText: ''});
this.saveUserTasks(this.state.noteArray)
}
}
deleteNote(key){
this.state.noteArray.splice(key,1);
this.setState({noteArray: this.state.noteArray})
this.saveUserTasks(this.state.noteArray)
}
goToNoteDetail=(key)=>{
this.props.navigation.navigate('DetailsScreen', {
selectedTask: this.state.noteArray[key],
});
}
}
in detail view I have this method which is similar to add note in main class:
export default class Details extends React.Component {
render() {
const { navigation } = this.props;
const selectedTask = navigation.getParam('selectedTask', 'task');
return(
<View key={this.props.keyval} style={styles.container}>
<TouchableOpacity onPress={this.saveEdit.bind(this)} style={styles.saveButton}>
<Text style={styles.saveButtonText}> save </Text>
</TouchableOpacity>
</View>
);
}
saveEdit(){
let selectedItem = { 'creationDate': selectedTask['creationDate'],
'taskName': selectedTask['taskName'],
'dueDate': this.state.dueData}
this.props.navigation.state.params.saveEdit(selectedItem)
}
}
How can I change my props in any component?
First of all you shouldn't call this.getUserTasks() in the render method because the function has this.setState which is bad and could end in a endless loop I guess or at least effect in worse performance. You could instead call it in componentDidMount:
componentDidMount = () => {
this.getUserTasks();
}
Or alternatively call already in constructor but I prefer the first option:
constructor(props){
super(props);
this.state = {
noteArray: [],
noteText: '',
dueDate: ''
};
this.getUserTasks()
}
this.props.noteArray.push({.. is probably undefined because you aren't passing it down any where. (Didn't see any reference in your snippet). I guess I would implement the saveEdit function in the Main.js component and simply pass it down to the navigation route and call the function in Details component by accessing the navigation state props:
Update
goToNoteDetail=(key)=>{
this.props.navigation.navigate('DetailsScreen', {
// selectedTask: this.state.noteArray[key],
selectedItem: key,
saveEdit: this.saveEdit
});
}
saveEdit(selectedItem){
const selectedTask = this.state.noteArray[selectedItem]
this.state.noteArray.push({
'creationDate': selectedTask['creationDate'],
'taskName': selectedTask['taskName'],
'dueDate': this.state.dueData
});
this.setState({noteArray:this.state.noteArray})
this.setState({dueData: 'YYYY/MM/DD'});
this.saveUserTasks(this.state.noteArray)
}
And then call saveEdit in Details Component:
saveSelectedItem = () => {
const { navigation } = this.props.navigation;
const {selectedItem, saveEdit} = navigation.state && navigation.state.params;
saveEdit(selectedItem)
}
I want to implement infinite scrollview in both the direction. Also the data should be loaded dynamically.
I am using SectionList component for list. I have implemented forward infinite scrolling. That means if user scroll down, the data will append to list automatically.
For that I have used onMomentumScrollEnd event. When user stops the scrolling, if the scroll is in Up direction, data will be appended at the End and if the scroll is in Down direction, data will be appended at the Top.
Now the problem is when I append the data at Top of list, It shift all the current list data to backward. I don't want to shift the current list even if the data is updated. Is there any way to do it.
This is my code:
import React, {Component} from 'react';
import {
Text,
View,
StyleSheet,
SectionList,
} from 'react-native';
import CardComponent from './CardComponent'
export default class Schedule extends Component {
constructor(props) {
super(props);
this.state = {
sectionData: [],
loading: false,
}
this.contentOffsetY = 0;
this._onScroll = this._onScroll.bind(this)
}
componentDidMount() {
this.setState({ sectionData: this.props.data })
}
renderItem = ({item}) => (
<CardComponent
data={item}
key={item}
/>
);
renderDateSeparator(text) {
return (
<Text style={{
paddingVertical: 15,
fontSize: 14,
flex: 1,
textAlign: 'center',
textAlignVertical: 'center',
}}>
{text}
<Text>
)
}
_onScroll(e){
let contentOffset = e.nativeEvent.contentOffset.y;
this.contentOffsetY < contentOffset ? this.loadMoreOnBottom() : this.loadMoreOnTop();
this.contentOffsetY = contentOffset;
}
loadMoreOnTop() {
this.setState({ lodaing: true });
// code to append data on top of list
this.setState({ lodaing: false });
}
loadMoreOnBottom() {
// code to append data at bottom of list
}
render() {
const sectionData = this.state.sectionData;
return(
<View style={{flex: 1}}>
<SectionList
onMomentumScrollEnd={this._onScroll}
automaticallyAdjustContentInsets={false}
itemShouldUpdate={false}
renderItem={this.renderItem}
renderSectionHeader={({section}) => this.renderDateSeparator(section.date)}
sections={sectionData}
stickySectionHeadersEnabled={false}
refreshing={this.state.loading}
onRefresh={() => this.loadMoreOnTop()}
onEndReachedThreshold={0.3}
onEndReached={() => this.loadMoreOnBottom()}
keyExtractor={(item) => item.key}
/>
</View>
)
}
}
Thanks in advance.
After so much of research, I have finally implemented the bidirectional infinite scroll view in react-native.
For the implementation, I have replaced my SectionList with FlatList, Because I want to use scrollToOffset method which is not properly working in SectionList.
I have used setInterval function of javaScript. It regularly checks weather the list need to be append from top or bottom.
This is my code:
import React, {Component} from 'react';
import {
Text,
View,
StyleSheet,
FlatList,
Dimensions,
} from 'react-native';
import CardComponent from './CardComponent'
let {height, width} = Dimensions.get('window');
export default class Schedule extends Component {
constructor(props) {
super(props);
this.state = {
listData: [],
}
this.contentOffsetY = 0;
this.pageOffsetY = 0;
this.contentHeight = 0;
this._onScroll = this._onScroll.bind(this);
this.loadMoreOnTop = this.loadMoreOnTop.bind(this);
this.loadMoreOnBottom = this.loadMoreOnBottom.bind(this);
}
componentDidMount() {
this.setState({ listData: this.props.data });
this._interval = setInterval(() => {
this.setState({ load: true });
}, 2000);
}
componentWillUnmount() {
clearInterval(this._interval);
}
renderItem = ({item}) => (
<CardComponent
data={item}
key={item}
/>
);
_onScroll(e){
let contentOffset = e.nativeEvent.contentOffset.y;
this.contentOffsetY < contentOffset ? this.loadMoreOnBottom() : this.loadMoreOnTop();
this.contentOffsetY = contentOffset;
}
scrollToOffset = (offset) => {
this.flatListRef ? this.flatListRef.scrollToOffset({animated: false, offset}) : null;
};
loadMoreOnTop() {
let newOffset;
// code to append data on top of list
// calculate newOffset:
newOffset = this.pageOffsetY + space required for new data.
this.contentOffsetY = newOffset;
this.scrollToOffset(newOffset);
}
loadMoreOnBottom() {
// code to append data at bottom of list
}
render() {
const listData = this.state.listData;
if(this.pageOffsetY < 600) {
this.loadMoreOnTop();
} else if((this.contentHeight - this.pageOffsetY) < (height * 1.5)){
this.loadMoreOnBottom();
}
return(
<View style={{flex: 1}}>
<FlatList
onScroll={(e) => {
this.pageOffsetY = e.nativeEvent.contentOffset.y;
this.contentHeight = e.nativeEvent.contentSize.height;
return null;
}}
onMomentumScrollEnd={this._onScroll}
automaticallyAdjustContentInsets={false}
itemShouldUpdate={false}
renderItem={this.renderItem}
data={listData}
refreshing={false}
onRefresh={() => this.loadMoreOnTop()}
onEndReachedThreshold={0.3}
onEndReached={() => this.loadMoreOnBottom()}
keyExtractor={(item) => item.key}
ref={(ref) => { this.flatListRef = ref; }}
animated={false}
/>
</View>
)
}
}