Is there a way to prevent automatic re-rendering when causing an event within a SectionList?
Problem Description:
This is my return function which contains all components in the renderer. Every time I press on the footerComponent in the Sectionlist, the whole screen re-renders and causes an auto-scroll to position 0.
return (
<SafeAreaView style={styles.container}>
<ImageBackground
source={require("../assets/stars.gif")}
style={{
width: Dim.width,
height: Dim.height,
}}
resizeMode="repeat"
>
{files ? <List /> : null}
<CustomModal
visible={visible}
onPressIn={() => startRecording()}
onPressOut={() => stopRecording()}
onExitPress={() => setVisible(false)}
onConfirm={() => sendToStorage(ID, len)}
disabled={!strgUri}
onDismiss={() => setRecording(null)}
modalButton={[
styles.modal,
pressed
? { backgroundColor: Colors.clay }
: { backgroundColor: Colors.aegean },
]}
progress={progress}
loadingVisible={sending}
/>
</ImageBackground>
</SafeAreaView>
);
and this is my Animated SectionList component.
const NewSectionList = Animated.createAnimatedComponent(SectionList);
const List = React.forwardRef((ref, props) => {
const respondRef = useRef();
const scrollRef = useRef().current;
function scrollToSection() {
if (respondRef.current) {
respondRef.current.measure((x, y, width, height, px, py) => {
console.log("height: ", height);
console.log("y: ", y);
setHeight(py);
});
console.log("scrolling to: ", height);
if (height != 0) {
setTimeout(() => {
console.log("wait height: ", height);
console.log("dim height: ", Dim.height);
console.log("1 height: ", Dim.height - height + Dim.height);
scrollRef?.scrollTo({
x: 0,
y: Dim.height - height + Dim.height,
animated: true,
});
}, 1000);
}
}
}
useEffect(() => {
scrollToSection();
});
const yVal = fadeAnim.interpolate({
inputRange: [0, 1],
outputRange: [900, 0],
});
const animStyle = {
transform: [
{
translateY: yVal,
},
],
};
return(
<Animated.View style={[animStyle]}>
<NewSectionList
ref={scrollRef}
stickySectionHeadersEnabled={false}
sections={files}
keyExtractor={(item) => item.id}
style={{
marginLeft: 10,
marginBottom: 110,
}}
renderItem={({ item }) => (
<View style={styles.cardContainer}>
<View
style={{
alignSelf: "flex-start",
position: "absolute",
marginTop: 5,
}}
>
<Text p4 dusk>
{new Date(item.timestamp).toDateString()}
</Text>
</View>
<TouchableOpacity
onPress={() => visitProfile(item.name, item.email)}
>
<Text p2 aegean>
{item.name}
</Text>
</TouchableOpacity>
<View styles={styles.cardTap}>
<TouchableNativeFeedback
onPress={() => {
playFile(item.url, item.id);
}}
>
<AntDesign name="stepforward" size={40} color={Colors.led} />
</TouchableNativeFeedback>
</View>
</View>
)}
renderSectionHeader={({
section: {
information,
origin,
userOrigin,
emailOrigin,
origID,
originDate,
},
}) => (
<View style={styles.headerContainer}>
<View
style={{
alignSelf: "flex-start",
position: "absolute",
marginTop: 5,
}}
>
<Text p3 led>
{new Date(originDate).toDateString()}
</Text>
</View>
<View style={styles.holder}>
<TouchableOpacity
onPress={() => visitProfile(userOrigin, emailOrigin)}
>
<Text h4 night>
{userOrigin}
</Text>
</TouchableOpacity>
</View>
<View style={styles.outerInfo}>
<Text p3 dusk style={styles.textInfo}>
{information ? information : ""}
</Text>
</View>
<View styles={styles.cardTap}>
<TouchableNativeFeedback
onPress={() => {
playFile(origin, origID);
}}
>
<AntDesign
name="stepforward"
size={40}
color={Colors.night}
/>
</TouchableNativeFeedback>
</View>
</View>
)}
renderSectionFooter={({ section: { docId, dataLen } }) => (
<TouchableOpacity
ref={respondRef}
style={styles.notPressed}
onPress={() => {
setVisible(true);
scrollToSection();
setID(docId);
setLen(dataLen);
}}
>
<Text p2 white>
add to convo
</Text>
</TouchableOpacity>
)}
refreshControl={<RefreshControl onRefresh={() => getData()} />}
/>
</Animated.View>
);
});
I tried to implement a scrollTo() method function to the position of the pressed button, but that is more of a hack than a real solution since it will keep re-rendering and scrolling. Also, the modal that I am trying to activate has other stateful components within it, causing more re-renders. Another attempted fix was trying to memo-ize the modal, however the official docs suggest that premature-optimization isn’t not a real solution and will lead to more bugs down the . Thank you.
Solution – 1
Create new useEffect to debug whether files
is always changed when your page is rerendered:
console.log('Screen in rerendered');
useEffect(() => {
console.log('`files` is changed', JSON.stringify(files, undefined, 2));
}, [files]);
The files shouldn’t be changed when you press footerComponent.
You may also want to wrap your <Animated.View></Animated.View>
with memo()
to prevent rerendering in its own component if the props are still equal.
Solution – 2
Ok so the solution to my problem was actually just to render the CustomModal component within my List component so when i needed to show the modal, I could set state in the List component, rather than sending it to the parent. Sending it to the parent caused a re-render in the children component every time I would set the state to a different value. So in general if you want to prevent a re-render between siblings, just render the state dependent component in the one with state.