I have list with Recyclerlistview where you can click on a item then a modal is open and in the modal are items with flatlist. So if I open the modal its rerenders 2-3x. And if I scroll or click its rerenders again. But why ? I use react.memo
Recyclerlistview
import * as React from 'react';
import { StyleSheet, Text, View, TouchableOpacity, Image, Dimensions } from 'react-native';
import { RecyclerListView, LayoutProvider, DataProvider } from 'recyclerlistview';
import I18n from 'i18n-js';
import { Feather } from '@expo/vector-icons';
import { Portal } from 'react-native-portalize';
import { ModalUppinfo } from '../../modal/uppinfo/modalUppinfo';
import { ModalDrops } from '../../modal/drops/modalDrops';
import { map1boss, map2boss, otherboss } from '../../../utils/mockData';
const { width, height } = Dimensions.get('window');
const Bosses = ({ data }) => {
const modals = Array.from({ length: 8 }).map(_ => React.useRef(null).current);
const [uppRef, setUppRef] = React.useState([]);
const [dropRef, setDropRef] = React.useState([]);
const openModalDrop = React.useCallback((dropdata) => {
setDropRef(dropdata);
modals[3].open();
}, [modals]);
const provider = React.useMemo(() => {
return new DataProvider(
(r1, r2) => {
return r1 !== r2;
}, index => {
return 'index: ' + index;
}
)
}, []);
const dataProvider = React.useMemo(() => {
return provider.cloneWithRows((data === 1) ? map1boss : (data === 2) ? map2boss : otherboss);
}, [provider, map1boss, map2boss, otherboss]);
const layoutProvider = new LayoutProvider((i) => {
return dataProvider.getDataForIndex(i).type;
}, (type, dim) => {
switch(type) {
case 'NORMAL':
dim.height = 250;
dim.width = width * 0.9;
break;
default:
dim.height = 0;
dim.width = 0;
break;
}
});
const Item = React.memo(({ name, image, level, translateID, upp, sell_price, dropinfo }) => (
<View style={styles.item}>
<Text style={styles.name}>{I18n.t(translateID)}</Text>
<View style={{justifyContent: 'center', alignItems: 'center'}}>
<Image source={image} resizeMode="contain" style={{height: 100, width: 100, marginBottom: 16 }} />
</View>
<View style={styles.itemFooter}>
<TouchableOpacity onPress={() => openModalUpp(upp)} style={styles.infoBtn}>
<Feather name="info" size={24} color="#333" />
<Text>Uppinfo</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => openModalDrop(dropinfo)} style={styles.infoBtn}>
<Feather name="info" size={24} color="#333" />
<Text>Dropinfo</Text>
</TouchableOpacity>
</View>
</View>
));
const rowRenderer = (type, data) => {
const { name, image, level, translateID, upp, sell_price, dropinfo } = data.item;
return (
<Item name={name} image={image} level={level} translateID={translateID} upp={upp} sell_price={sell_price} dropinfo={dropinfo} />
)
};
return (
<View style={styles.container}>
<RecyclerListView
dataProvider={dataProvider}
layoutProvider={layoutProvider}
rowRenderer={rowRenderer}
scrollViewProps={{showsVerticalScrollIndicator: false}}
style={{flex: 1, marginTop: 20}}
/>
<Portal>
<ModalDrops DropInfo={dropRef} ref={el => (modals[3] = el)} />
</Portal>
</View>
)
};
Modal Flatlist:
import * as React from 'react';
import { StyleSheet, Text, View, TouchableOpacity, Image, Dimensions } from 'react-native';
import { Modalize } from 'react-native-modalize';
import faker from 'faker';
const { width } = Dimensions.get('window');
export const ModalDrops = React.forwardRef(({ DropInfo }, ref) => {
console.log('Drops')
const Item = React.memo(({ name, image, index }) => {
console.log('rerender');
return (
<View style={styles.item}>
<TouchableOpacity style={{justifyContent: 'center', alignItems: 'center', paddingRight: DropInfo.monster.length -1 === index ? 36 : 0}}>
<Image source={{uri: faker.image.avatar()}} resizeMode="contain" style={{height: 80, width: 80, borderRadius: 8, marginBottom: 10}} />
</TouchableOpacity>
</View>
)
});
function renderItem({ item, index }) {
return <Item name={item.name} image={item.image} index={index} />
};
function key() {
return Math.random(10000).toString();
}
return (
<Modalize
ref={ref}
modalHeight={300}
flatListProps={{
data: DropInfo.monster,
style: {
padding: 8,
flex: 1
},
renderItem: renderItem,
horizontal: true,
ListEmptyComponent: () => {
return (
<View style={styles.itemEmpty}>
<Text>Nicht Droppbar</Text>
</View>
)
},
keyExtractor: key,
showsHorizontalScrollIndicator: false,
scrollEventThrottle: 16,
contentContainerStyle: {
justifyContent: 'center',
alignItems: 'center'
}
}}
/>
)
});
Video: https://gyazo.com/ff91667f7f0258320317aa7645a046cf ............................................................................................................................................................
From what I can see you've one major issue with the way you declare the Item
component, it's declared inside another component, thus is it redeclared any time the outer component renders. This effectively unmounts the previous "instance" and mounts a new "instance". It isn't technically rerendering, it's just flat out remounted.
Don't declare React components inside other React components. In doing so and trying to memoize any render results won't work since each render cycle creates and mounts a new component. Move the Item
declaration outside the components.
Bosses
const Item = React.memo(({ .... }) => (
<View style={styles.item}>
...
</View>
</View>
));
const Bosses = ({ data }) => {
...
const rowRenderer = (type, data) => {
const { name, image, level, translateID, upp, sell_price, dropinfo } = data.item;
return (
<Item
name={name}
image={image}
level={level}
translateID={translateID}
upp={upp}
sell_price={sell_price}
dropinfo={dropinfo}
/>
)
};
return (
<View style={styles.container}>
<RecyclerListView
dataProvider={dataProvider}
layoutProvider={layoutProvider}
rowRenderer={rowRenderer}
scrollViewProps={{showsVerticalScrollIndicator: false}}
style={{flex: 1, marginTop: 20}}
/>
<Portal>
<ModalDrops DropInfo={dropRef} ref={el => (modals[3] = el)} />
</Portal>
</View>
)
};
ModalDrops
const Item = React.memo(({ name, image, index }) => {
return (
<View style={styles.item}>
...
</View>
)
});
export const ModalDrops = React.forwardRef(({ DropInfo }, ref) => {
function renderItem({ item, index }) {
return <Item name={item.name} image={item.image} index={index} />
};
...
return (
<Modalize
ref={ref}
modalHeight={300}
flatListProps={{
data: DropInfo.monster,
style: {
padding: 8,
flex: 1
},
renderItem: renderItem,
horizontal: true,
ListEmptyComponent: () => {
return (
<View style={styles.itemEmpty}>
<Text>Nicht Droppbar</Text>
</View>
)
},
keyExtractor: key,
showsHorizontalScrollIndicator: false,
scrollEventThrottle: 16,
contentContainerStyle: {
justifyContent: 'center',
alignItems: 'center'
}
}}
/>
)
});