React native | websocket reload on rerender - reactjs

I'm building small application about smart home using websocket. When i press device icon to toggle it, websocket sends message and updates state device is enabled or not. Problem is after updating component, it creates new websocket connection, not saving old one. Can anyone give some advice? What am i doing wrong?Better to use other npm lib? Here is code:
`
import React, { useRef, useState,useEffect } from 'react'
import { StyleSheet, Text, View, Pressable} from 'react-native'
import LightBulbOn from '../../assets/icons/light-bulb.svg'
import Power from '../../assets/icons/power.svg'
export default function Light({ ip,id,openModal }) {
const client = React.useRef()
useEffect(() => {
client.current = new WebSocket('ws://192.168.0.107/ws')
client.current.onopen = (message) => {
let data = JSON.parse(message.data)
alert(data)
let arr = [...devices]
arr[0].condition = data.light1
arr[1].condition = data.light2
setDevices(arr)
}
client.current.onmessage = message =>{
let data = JSON.parse(message.data)
let arr = [...devices]
arr[0].condition = data.light1
arr[1].condition = data.light2
setDevices(arr)
}
client.current.onerror = message =>{
alert(message)
}
},[ip])
function sendMessage(id){
if(!client.current) {
alert('not client')
// client.current = new WebSocket('ws://192.168.0.105/ws')
return
}
let message = devices[id].condition?'->off':'->on'
client.current.send("light" + devices[id].id + message)
}
const [devices, setDevices] = useState([
{
id: 1,
deviceName: 'ნათურა 1',
condition: 0,
},
{
id: 2,
deviceName: 'ნათურა 2',
condition: 0
}
])
return (
<View style={styles.container} >
<Pressable style={styles.lightContainer} onLongPress={()=>openModal(id)} onPress = {()=> sendMessage(0)}>
<View style={styles.containerRow}>
<LightBulbOn width='30' height='30' />
<Power width='20' height='20' />
</View>
<View style={styles.description}>
<Text style={{ fontWeight: 'bold' }}>{devices[0].deviceName}</Text>
<Text>{devices[0].condition?'ჩართული':'გამორთული'}</Text>
</View>
</Pressable>
<Pressable style={styles.lightContainer} onLongPress={()=>openModal(id)} onPress = {()=> sendMessage(1)}>
<View style={styles.containerRow}>
<LightBulbOn width='30' height='30' />
<Power width='20' height='20' />
</View>
<View style={styles.description}>
<Text style={{ fontWeight: 'bold' }}>{devices[1].deviceName}</Text>
<Text>{devices[1].condition?'ჩართული':'გამორთული'}</Text>
</View>
</Pressable>
</View>
)
}
`
Thanks in advance

Suggest you can try this one instead:
https://github.com/Sumit1993/react-native-use-websocket
And if you pass nothing to useRef, it will return you undefined that can not serializable which might cause the problem.

Related

Why navigation go back doesn't destroy my video list screen?

i 'm trying to go back to my home screen from VideoListScreen destroying VideoListScreen . I tried multiple solutions such as goBack() , replace() and reset() methods in react native navigation.
I want to destroy VideoListScreen so i can reset the state variables ( in my case viewedTimes ) but every time i click on every video and i return to home screen , when i navigate to videoListScreen again it navigate immediately back to home screen because my video list screen remained same and doesn't reset.
i use UseFocusEffect so every time i go back to videoListScreen the method onVideoExit controls if all video are viewed.
if you have any question i am ready to clarify thank you very much for your help
here is my code of homeScreen:
import LanguageSelector from '#components/LanguageSelector';
import i18n, {T, __} from '#config/i18n';
import React, {useRef, useState} from 'react';
import {
CommonActions,
useFocusEffect,
useNavigation,
} from '#react-navigation/native';
import {
ImageBackground,
Linking,
NativeEventEmitter,
NativeModules,
Pressable,
Text,
View,
} from 'react-native';
import languageList from '#assets/arrays/Language.json';
import {appStyles, appColors} from '#assets/styles/style';
import CoinboxCollector from 'react-native-coinbox-collector/src/CoinboxCollector';
import KeypadModal from '#components/KeypadModal';
export default function HomeScreen() {
const [currentLanguage, setCurrentLanguage] = useState(i18n.language);
const [showKeypadModal, setKeypadModal] = useState(false);
const secretButtonCountToReach = 6;
let secretButtonClickedCount = 0;
let coinInserted: number = 0;
const coinValueStartExperience = 2;
const navigation = useNavigation();
const EventEmitted = useRef<NativeEventEmitter>();
let timeoutId: any;
function timerCoinInserted() {
if (coinInserted > 2) return;
coinInserted = 0;
console.log('reset money', coinInserted);
}
async function onCoinDetected(coin: any) {
coinInserted = coinInserted + parseFloat(coin);
timeoutId = setTimeout(timerCoinInserted, 120000);
if (coinInserted < coinValueStartExperience) return;
console.log('you pay for the experience');
coinInserted = 0;
CoinboxCollector.stop();
navigation.navigate('VideoListScreen');
}
const initCoinbox = async () => {
return await CoinboxCollector.initialize();
};
useFocusEffect(
React.useCallback(() => {
initCoinbox();
let listener = EventEmitted.current;
listener = new NativeEventEmitter(NativeModules.CoinBoxCollector);
let listenerEvent = listener.addListener(
'onReceivedCoin',
onCoinDetected,
);
return () => {
listenerEvent?.remove();
CoinboxCollector.stop();
clearTimeout(timeoutId);
};
}, []),
);
return (
<View style={appStyles.flex}>
<ImageBackground
source={require('#assets/images/cappella-sistina.jpeg')}
style={appStyles.imageScreen}>
<View style={appStyles.homeOverlay}>
<LanguageSelector
languages={languageList}
currentSelectedLanguage={currentLanguage}
onLanguageChanged={(newLanguage: string) => {
setCurrentLanguage(newLanguage);
}}
/>
<View style={appStyles.topTabText}>
<Text style={appStyles.textScreen}>{__(T.messages.welcome)}</Text>
<Text style={[appStyles.italicText, {color: appColors.white}]}>
{__(T.messages.interact)}
</Text>
<Text style={appStyles.textScreen}>€ 2,00</Text>
{/* <Text style={appStyles.textScreen}>€ {coinInsrted}</Text> */}
</View>
<Pressable
onPress={onSecretButtonCounter}
style={appStyles.settingsButton}
/>
</View>
</ImageBackground>
{showKeypadModal && (
<KeypadModal onCancel={onCancel} onConfirm={onConfirmPassword} />
)}
</View>
);
}
here is my code from the videoListScreen:
import {appStyles} from '#assets/styles/style';
import Card from '#components/Card';
import {Video} from '#types';
import React, {useEffect, useState} from 'react';
import {FlatList, Pressable, Text, View} from 'react-native';
import {videosList} from '#assets/arrays/Videos';
import languageList from '#assets/arrays/Language.json';
import Footer from '#components/Footer';
import FakeCard from '#components/FakeCard';
import LanguageSelector from '#components/LanguageSelector';
import i18n from '#config/i18n';
import {
CommonActions,
useFocusEffect,
useNavigation,
} from '#react-navigation/native';
export default function VideoListScreen() {
const [videos, setVideos] = useState<Video[]>(videosList);
const [clickedVideo, setClickedVideo] = useState<Video>();
const [currentLanguage, setCurrentLanguage] = useState(i18n.language);
const navigation = useNavigation();
const renderItem = ({item, index}: {item: Video; index: number}) => {
if (!item.fake)
return (
<Pressable onPress={() => onVideoPlayed(item, index)}>
<Card video={item} currentLanguage={currentLanguage} />
</Pressable>
);
return <FakeCard />;
};
function onVideoPlayed(video: Video, index: number) {
if (video.viewedTimes && video.viewedTimes > 0) return;
let newVideos = videos;
newVideos[index].viewedTimes++;
setVideos(newVideos);
setClickedVideo(newVideos[index]);
}
function onVideoExit() {
if (videos.filter(video => video.viewedTimes < 1).length >= 1) return;
navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [{name: 'HomeScreen'}],
}),
);
//navigation.goBack();
// navigation.dispatch(StackActions.replace('HomeScreen'));
}
useFocusEffect(
React.useCallback(() => {
onVideoExit();
console.log('comincia il conto alla rovescia');
const timeoutId = setTimeout(() => {
navigation.goBack();
}, 120000);
return () => {
clearTimeout(timeoutId);
onVideoExit();
};
}, []),
);
useEffect(() => {
if (!clickedVideo) return;
navigation.navigate('PlayerScreen', {
video: clickedVideo,
currentLanguage,
});
}, [clickedVideo]);
return (
<View style={{flex: 1}}>
<View style={appStyles.headerContainer}>
<Text style={appStyles.titleScreen}>
TAKE A TOUR OF THE BASILICA OF SANTA MARIA NOVELLA
</Text>
<LanguageSelector
languages={languageList}
currentSelectedLanguage={currentLanguage}
onLanguageChanged={(newLanguage: string) => {
setCurrentLanguage(newLanguage);
}}
/>
</View>
{/* <Pressable
onPress={() => {
navigation.dispatch(StackActions.replace('HomeScreen'));
}}
style={{
backgroundColor: 'red',
width: 80,
height: 80,
zIndex: 10000,
}}>
<Text>Ciao</Text>
</Pressable> */}
<FlatList
data={videos}
keyExtractor={item => item.title}
renderItem={({item, index}) => renderItem({item, index})}
numColumns={4}
contentContainerStyle={appStyles.cardListStyle}
/>
<Footer />
</View>
);
}

How to Open URL "on click" after Scanning a QR Code in React-Native Android App

I am new to React-Native & created a QR Scanner App. After scanning want to open url onClick/onPress.
-- Here is the code
onSuccess = (e) => {
setResult(e.data)
setScan(false)
}
// Start scanner
startScan = () => {
setScan(true)
setResult()
}
<QRCodeScanner
reactivate={true}
showMarker={true}
ref={(node) => { this.scanner = node }}
onRead={this.onSuccess}
topContent={
<Text style={styles.centerText}>
Scan your QRCode!
</Text>
}
bottomContent={
<TouchableOpacity style={styles.buttonTouchable} onPress={() => setScan(false)}>
<Text style={styles.buttonText}>Cancel Scan</Text>
</TouchableOpacity>
}
/>
Let me help you
Your code might look like this:
import { useState } from 'react';
import { Text } from 'react-native';
const App = () => {
const [scan, setScan] = useState(false);
const [result, setResult] = useState();
function onSuccess(e){
setResult(e.data);
setScan(false);
}
function onURLPress(){
Linking.openURL(result);
}
return (
<QRCodeScanner
reactivate={true}
showMarker={true}
ref={(node) => { this.scanner = node }}
onRead={this.onSuccess}
topContent={
<Text style={styles.centerText}>
Scan your QRCode!
</Text>
}
bottomContent={
<Text style={[styles.buttonText, styles.buttonTouchable]} onPress={() => setScan(false)}>Cancel Scan</Text>
}
/>
{/* This is where you show the generated url */}
{result && <Text onPress={onURLPress}>{result}</Text>}
)
}
Note that you can change your bottomContent Text to use onPress as well, instead of using TouchableOpacity.

How to save the QR Data and copy my clipboard?

I just have some question.
First, I'm beginner... Very sorry.
I have been developed the react-native app (iOS) for recognize the QR Code.
I already have been succeed the recognize the QR Code on my App.
But My Plan is..
First, I scan the QR Code and I want to save the QR data.
For the purpose, there are many QR Code in the warehouse above many box and it is included Serial Number.
After scanning, I will continue the scan until I want to stop it. (In conclusion, I scan many times.)
I just thought "Let's save the Serial Number at array.
Second, I save the serial number through the scanning and I want to copy our clipboard.
Also, I want to print in my App.
How can I implement this? I have no idea about that.
Code is here.
import { StatusBar } from 'expo-status-bar';
import React, {useState, useEffect} from 'react'
import { StyleSheet, Text, View, Button } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
export default function App() {
const [hasPermisson, setHasPermisson] = useState(null);
const [scanned, setScanned] = useState(false);
const [text, setText] = useState('Not yet scanned')
const askForCameraPermisson = () => {
(async () => {
const {status} = await BarCodeScanner.requestPermissionsAsync();
setHasPermisson(status == 'granted')
})()
}
useEffect(() => {
askForCameraPermisson ();
}, [])
const handleBarCodeScanned = ({type, data}) => {
setScanned(true);
setText(text);
state = {
sn : [
{
type : type,
data : data
},
{
type : type,
data : data
}
]
}
console.log(state)
}
if (hasPermisson === null) {
return (
<View style={styles.container}>
<Text>Requesting for camera permisson</Text>
<StatusBar style="auto" />
</View>
)
}
if(hasPermisson === false) {
return (
<View style={styles.container}>
<Text>No acess to camera</Text>
<Button title={'Allow Camera'} onPress={() => askForCameraPermisson()}/>
</View>
)
}
return (
<View style={styles.container}>
<View style={styles.barcodebox}>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style = {{ height:400, width:400}} />
</View>
<Text style={styles.maintext}>{text}</Text>
{scanned && <Button title={'scan again?'} onPress={( ) => setScanned(false)} color='tomato'/>}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
barcodebox: {
alignItems: 'center',
justifyContent: 'center',
height: 300,
width:300,
overflow:'hidden',
borderRadius:30,
backgroundColor:'tomato'
},
maintext: {
fontSize :16,
margin:20
}
});

React Native pincode lock screen create. useState error

I am trying to create a mobile lock screen with numbers to user for entering the pincode.
when the user press the number buttons values should be entered to the array that I have created.
when the numbers are entered, a style property is changed.
here is the code
import React from "react";
import { Alert, StyleSheet, Text, Touchable, TouchableHighlight, TouchableOpacity,useState, View } from "react-native";
import Loading from './Loading';
const Buttons = () => {
this.state = {
passcode: ['','','','']
}
_presControl = num =>{
let tempCode = this.state.passcode;
for(var i = 0; i<tempCode.length;i++){
if(tempCode[i] == ''){
tempCode[i] = num;
break;
}else{
continue;
}
}
this.setState({passcode:tempCode});
};
let nopad = [
{id:1},
{id:2},
{id:3},
{id:4},
{id:5},
{id:6},
{id:7},
{id:8},
{id:9},
{id:0}
];
return(
<View >
<View style={styles.boxcontaner} >
{this.state.passcode.map(p =>{
let style= p !=''? styles.box2:styles.box1;
return <View style={style}></View>
})}
</View>
<View style={styles.noBox}>
{nopad.map(num =>{
return(
<TouchableOpacity style={styles.box}
key={num.id}
onPress={this._presControl(num.id)} >
<Text style={styles.title}>{num.id}</Text>
</TouchableOpacity>
);
})}
</View>
</View>
);
}
const styles = StyleSheet.create({
box1: {
width:13,
height:13,
borderRadius:13,
borderWidth:1,
borderColor:'gray'
},
box2: {
width:13,
height:13,
borderRadius:13,
borderWidth:1,
borderColor:'gray',
backgroundColor:'red'
},
box: {
width:70,
height:70,
borderRadius:70,
borderWidth:1,
borderColor:'#F2F3F4',
alignItems:'center',
backgroundColor:'#F2F3F4',
justifyContent:'center',
alignItems:'center'
},
boxcontaner:{
flexDirection:'row',
alignItems:'center',
justifyContent:'space-between',
marginLeft:40,
marginRight:40,
marginTop:10,
},
noBox:{
alignItems:'center',
justifyContent:'center',
marginTop:100,
flexDirection:'row',
flexWrap:'wrap',
marginLeft:20,
width:270,
height:200,
}
});
export default Buttons;
But when I run the code it says
_this._presControl is not a function. (In '_this._presControl(num.id)', '_this._presControl' is undefined)
what is the error. How can I solve this please ?
You need to create a copy of your array so that you can update the state when a user enters the pin.
_presControl = num =>{
let tempCode = this.state.passcode;
for(var i = 0; i<tempCode.length;i++){
if(tempCode[i] == ''){
tempCode[i] = num;
break;
}else{
continue;
}
}
var newPinCode = [...tempCode];
this.setState(newPinCode);
};
You can not use class methods inside of body of the functional component. Instead you should call you function like that:
<TouchableOpacity
style={styles.box}
key={num.id}
onPress={()=>_presControl(num.id)} >
<Text style={styles.title}>{num.id}</Text>
</TouchableOpacity>

How to access next url from browser click from react-native-webview

I have a webview source that I have opened using react-native-webview, I want to access any and every url and console it whenever I click on the webview, so that I can use it as source for another webview. However am unable to figure how to do it
I tried using NavigationStateChange and onShouldLoadSTartwithrequest but that did not help.
below is my code
import React, {useState, useRef, useEffect} from 'react';
import {WebView} from 'react-native-webview';
import {
View,
SafeAreaView,
ActivityIndicator,
StyleSheet,
TouchableOpacity,
Text,
Linking,
Alert,
BackHandler,
} from 'react-native';
import Footer from '../components/Footer';
import {useBackHandler} from '#react-native-community/hooks';
import OnlineConsultationWebviewScreen from './OnlineConsultationWebviewScreen';
export default function ConsultationHomeScreen(props) {
const uri = props.route.params.uri;
const [canGoBack, setCanGoBack] = useState(false);
const [canGoForward, setCanGoForward] = useState(false);
const [currentUrl, setCurrentUrl] = useState('');
const webviewRef = useRef(null);
const renderLoadingView = () => {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<ActivityIndicator size="large" />
</View>
);
};
const onMessage = (e) => {
// retrieve event data
var data = e.nativeEvent.data;
// maybe parse stringified JSON
try {
data = JSON.parse(data);
} catch (e) {}
// check if this message concerns us
if ('object' == typeof data && data.external_url_open) {
// proceed with URL open request
return Alert.alert(
'External URL',
'Do you want to open this URL in your browser?',
[
{text: 'Cancel', style: 'cancel'},
{text: 'OK', onPress: () => Linking.openURL(data.external_url_open)},
],
{cancelable: false},
);
}
};
const jsCode = `!function(){var e=function(e,n,t){if(n=n.replace(/^on/g,""),"addEventListener"in window)e.addEventListener(n,t,!1);else if("attachEvent"in window)e.attachEvent("on"+n,t);else{var o=e["on"+n];e["on"+n]=o?function(e){o(e),t(e)}:t}return e},n=document.querySelectorAll("a[href]");if(n)for(var t in n)n.hasOwnProperty(t)&&e(n[t],"onclick",function(e){new RegExp("^https?://"+location.host,"gi").test(this.href)||(e.preventDefault(),window.postMessage(JSON.stringify({external_url_open:this.href})))})}();`;
return (
<SafeAreaView style={{flex: 1}}>
<WebView
source={{
uri: uri,
}}
renderLoading={renderLoadingView}
javaScriptEnabled={true}
domStorageEnabled={true}
startInLoadingState={true}
ref={webviewRef}
injectedJavaScript={jsCode}
onMessage={onMessage}
onError={console.error.bind(console, 'error')}
// onShouldStartLoadWithRequest={(event) => {
// if (event.url !== uri ){
// Linking.openURL(event.url);
// console.log('Event', event.url);
// return false;
// }
// return true;
// }}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
tabBarContainer: {
padding: 20,
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: '#b43757',
},
button: {
color: 'white',
fontSize: 24,
},
});
Am stuck with this since long, please let me know how do I access any and every click and console it, so that I can sue it later as a new source for Webview.
Any suggestion would be great.
Try this :
<WebView
ref="webview"
source={uri}
onNavigationStateChange={this._onNavigationStateChange.bind(this)}
javaScriptEnabled = {true}
domStorageEnabled = {true}
injectedJavaScript = {this.state.cookie}
startInLoadingState={false}
/>
Add this function :
_onNavigationStateChange(webViewState){
console.log(webViewState.url)
}
FYI webviewState object consists of, use the url property:
{
canGoBack: bool,
canGoForward: bool,
loading: bool,
target: number,
title: string,
url: string,
}
let me know if it helps

Resources