Last active
July 8, 2022 12:50
-
-
Save boblitex/3098cd0d50bf09a800ecd5b7b45cafad to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable no-sequences */ | |
/* eslint-disable no-return-assign */ | |
/* eslint-disable no-empty */ | |
/* eslint-disable no-plusplus */ | |
/* eslint-disable no-unsafe-finally */ | |
/* eslint-disable no-undef */ | |
/* eslint-disable global-require */ | |
import React, { useState, useEffect, useRef, useCallback } from 'react'; | |
import { FlatList, View, Text, Pressable, Animated, Platform } from 'react-native'; | |
import NetInfo, { useNetInfo } from '@react-native-community/netinfo'; | |
import Ping from 'react-native-ping'; | |
import DevicesIcon from '../../../assets/svg_images/devices.svg'; | |
import Container from '../../../components/layout/Container.component'; | |
import { ScannedRowItem } from '../rowItems/ScannedRow.item'; | |
import Button from '../../../components/button/Button.component'; | |
import UploadToolsTest from '../../../components/uploadToolsTest/uploadToolsTest'; | |
import * as colors from '../../../constants/colors'; | |
import { styles } from './ScanNetwork.style'; | |
import { onStartShouldSetResponder } from '../../../hooks/onStartShouldSetResponder'; | |
export default function ScanNetworkScreen({ show = false, setNetworkScanner = () => {}, events, trigger = false }) { | |
const animation = useRef(new Animated.Value(0)); | |
const stop = useRef(false); | |
const { details: netInfo } = useNetInfo(); | |
const [idx, setIdx] = useState(0); | |
const [data, setData] = useState([]); | |
const [status, setStatus] = useState(false); | |
const [isLoading, setIsLoading] = useState(false); | |
const [type, setType] = useState(null); | |
const [showUploadModal, setShowUploadModal] = useState(false); | |
const [jsonData, setJsonData] = useState([]); | |
const [showButton, setShowButton] = useState(false); | |
const compareIPAddresses = (a, b) => { | |
const numA = Number( | |
a.name | |
.split('.') | |
.map((num, id) => num * Math.pow(2, (3 - id) * 8)) | |
.reduce((ip, v) => ((ip += v), ip), 0), | |
); | |
const numB = Number( | |
b.name | |
.split('.') | |
.map((num, id) => num * Math.pow(2, (3 - id) * 8)) | |
.reduce((ip, v) => ((ip += v), ip), 0), | |
); | |
return numA - numB; | |
}; | |
const scanNetwork = async () => { | |
const ip = netInfo?.ipAddress; | |
const substr = (ip ?? '---').lastIndexOf('.'); | |
const ipRange = (ip ?? '---').substring(0, substr + 1); | |
for (let i = 0; i < 256; i++) { | |
if (!stop.current) { | |
const tmpIP = !stop.current ? ipRange + i.toString() : ''; | |
try { | |
// let timeoutValue = 500; | |
// if (Platform.OS === 'ios') { | |
// timeoutValue = 1000; | |
// } | |
const tmpMS = !stop.current && (await Ping.start(tmpIP, { timeout: 300 })); | |
!stop.current | |
? setData((prev) => [ | |
...prev, | |
{ | |
id: i, | |
image: DevicesIcon, | |
name: tmpIP, | |
pingms: tmpMS, | |
value: '---', | |
}, | |
]) | |
: null; | |
} catch (error) { | |
// | |
} finally { | |
!stop.current ? setIdx((prev) => prev + 1) : null; | |
} | |
} else { | |
break; | |
} | |
} | |
}; | |
useEffect(() => () => (stop.current = true), []); | |
useEffect(() => { | |
NetInfo.fetch().then((state) => { | |
setType(state.type); | |
}); | |
return () => null; | |
}, [type]); | |
const runTest = () => { | |
setIsLoading(true); | |
scanNetwork(); | |
setStatus(true); | |
stop.current = false; | |
}; | |
const stopTest = async () => { | |
setStatus(false); | |
setIsLoading(false); | |
stop.current = true; | |
}; | |
const search = () => { | |
if (idx === 0 && !status && netInfo && !isLoading) { | |
runTest(); | |
} else if ((idx !== 0 && stop.current) || (idx >= 255 && !stop.current)) { | |
setIdx(0); | |
setData([]); | |
setShowButton(false); | |
stop.current = false; | |
setTimeout(() => runTest(), 0); | |
} else if (idx !== 0 && status && isLoading && !stop.current) { | |
stopTest(); | |
} | |
}; | |
const sendDataToServer = useCallback(() => { | |
const info = data.map(({ id, image, value, ...rest }) => ({ | |
ipAddress: rest.name, | |
pingResponse: rest.pingms, | |
})); | |
// show the Upload Tools Test Modal | |
setShowUploadModal(true); | |
// set the data to upload | |
setJsonData(info); | |
}, []); | |
const percentage = | |
((idx / 256) * 100).toFixed(2).split('.')[1] !== '00' | |
? `${((idx / 256) * 100).toFixed(2)}%` | |
: `${((idx / 256) * 100).toFixed(0)}%`; | |
useEffect(() => { | |
Animated.timing(animation.current, { | |
toValue: 100, | |
duration: 500, | |
useNativeDriver: false, // <-- Add this | |
}).start(); | |
}, []); | |
useEffect(() => { | |
if (idx === 255) { | |
setIsLoading(false); | |
setNetworkScanner(true); | |
setShowButton(true); | |
stop.current = false; | |
} | |
return () => null; | |
}, [idx, netInfo]); | |
useEffect(() => { | |
if (trigger && events) { | |
events.shareScanNetworkResult = sendDataToServer; | |
} | |
}, [trigger]); | |
return ( | |
<Container style={{ flex: 1, backgroundColor: !show ? colors.brandSuperLightGrey : colors.bgWhite }}> | |
<View | |
onStartShouldSetResponder={onStartShouldSetResponder} | |
style={[ | |
styles.card, | |
{ | |
shadowColor: !show ? colors.textBlack : colors.bgWhite, | |
margin: !show ? 16 : 15, | |
paddingVertical: !show ? 32 : 15, | |
}, | |
]} | |
> | |
<UploadToolsTest | |
showUploadModal={showUploadModal} | |
data={JSON.stringify(jsonData)} | |
tagFrom="scannetwork" | |
setShowUploadModal={(val) => setShowUploadModal(val)} | |
/> | |
<Pressable | |
onPress={() => type === 'wifi' && search()} | |
style={[ | |
styles.btn, | |
{ | |
opacity: type !== 'wifi' ? 0.3 : 1.0, | |
borderColor: type === 'wifi' ? colors.brandGreenLight : colors.brandGrayLight, | |
}, | |
]} | |
> | |
<View | |
style={[styles.btnContainer, { borderColor: type === 'wifi' ? colors.brandGreen : colors.brandMediumGrey }]} | |
> | |
<Text style={styles.btnText}>{!isLoading ? 'Start' : 'Stop'}</Text> | |
</View> | |
</Pressable> | |
{idx > 0 || showButton ? ( | |
<> | |
<Text style={styles.cardText}>{percentage}</Text> | |
<View style={styles.progressBar}> | |
<Animated.View | |
style={[styles.progressBarFill, { backgroundColor: colors.brandDarkBlue, width: percentage }]} | |
/> | |
</View> | |
<Text style={styles.cardSubtext}> | |
{data.length > 0 && !isLoading ? 'Discovery completed.' : null} | |
{isLoading ? 'Discovering devices now...' : null} | |
</Text> | |
</> | |
) : null} | |
</View> | |
<View | |
style={[ | |
styles.card, | |
{ | |
shadowColor: !show ? colors.textBlack : colors.bgWhite, | |
margin: !show ? 16 : 0, | |
padding: 16, | |
flex: 1, | |
display: data.length > 0 ? 'flex' : 'none', | |
}, | |
]} | |
> | |
<FlatList | |
style={styles.list} | |
data={data.sort(compareIPAddresses)} | |
renderItem={({ item }) => <ScannedRowItem item={item} />} | |
keyExtractor={(item) => item.id} | |
/> | |
{showButton && !show ? ( | |
<Button | |
width={100} | |
theme="#00b4b0" | |
mode="contained" | |
label="Share with Mweb" | |
onPress={sendDataToServer} | |
style={{ marginTop: 10, marginBottom: 0 }} | |
/> | |
) : null} | |
</View> | |
</Container> | |
); | |
} |
okay thanks, Ryan, let me give these suggestions a shot and see.
If you're still having issues, feel free to put together an MRE or give me temporary access to your repo -- and I'll try to run and debug on my end.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I see where the
GBPing
object is being allocated on every ping and the Xcode project appears to have ARC enabled but it's possible that they are not being deallocated as fast as you're creating them.The module seems to be using a secondary queue correctly though it's a shared queue for every instance. Not sure if that's an issue or not.
Since this is a function component, have you checked to see if it's re-rendering unnecessarily and if that's causing more ping requests to be generated than you expect?
I'm not saying that this is necessarily the cause but here are some things I've observed that may be causing extra re-renders:
onPress
handler contains an anonymous function so it's a continuously regenerated and therefore changing prop that could be invoking extra re-renders; try changing it toonPress={sendDataToServer}
sendDataToServer
is re-created on every re-render because it is defined usingconst sendDataToServer = ...
instead offunction
. You should therefore wrap it in auseCallback
, otherwise it keeps changing the value of theonPress
prop and can cause un-necessary re-renders.compareIPAddresses
is a pure, stateless function; it doesn't need to exist inside theScanNetworkScreen
component (and get re-created on every re-render)Personally, I'd investigate and quell any unnecessary re-rendering before digging further into the memory issue as 1) this is a frequent cause of these kinds of issues with function components and 2) it's less likely the issue is in the native component if it's an established library that's been around for a while.