Skip to content

Instantly share code, notes, and snippets.

@boblitex
Last active July 8, 2022 12:50
Show Gist options
  • Save boblitex/3098cd0d50bf09a800ecd5b7b45cafad to your computer and use it in GitHub Desktop.
Save boblitex/3098cd0d50bf09a800ecd5b7b45cafad to your computer and use it in GitHub Desktop.
/* 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>
);
}
@techrah
Copy link

techrah commented Jul 1, 2022

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:

  • the 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 to onPress={sendDataToServer}
  • sendDataToServer is re-created on every re-render because it is defined using const sendDataToServer = ... instead of function. You should therefore wrap it in a useCallback, otherwise it keeps changing the value of the onPress prop and can cause un-necessary re-renders.
  • compareIPAddresses is a pure, stateless function; it doesn't need to exist inside the ScanNetworkScreen 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.

@boblitex
Copy link
Author

boblitex commented Jul 1, 2022

okay thanks, Ryan, let me give these suggestions a shot and see.

@boblitex
Copy link
Author

boblitex commented Jul 1, 2022

Hey Ryan, tried that. updated the gist (see diff), still crashes after 4 scans . attached a screenshot with some Perf metrics.
IMG_4688

tried everything I know.

@techrah
Copy link

techrah commented Jul 8, 2022

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