React Native — плоские списки повторно отображают запомненные компоненты (React.memo не работает)

#javascript #reactjs #react-native #memoization

Вопрос:

Основная проблема

Мой плоский список игнорирует детские воспоминания.

Объяснение

Я реализовал пользовательский компонент «Сетка», который просто отображает данные в плоском списке с макетом сетки.

Вот код:

 export default function Grid({  data = [],  keyExtractor,  numColumns = 4,  initialNumToRender,  renderItem,  itemMargin = StyleSheet.hairlineWidth,  isLoading,  onEndReachedThreshold = 0.5,  onEndReached,  ...props }) {  const renderGridItem = (item) =gt; {  const { index } = item;   const { width } = Dimensions.get("window");   const size = Math.floor(  PixelRatio.roundToNearestPixel(  (width - itemMargin * (numColumns - 1)) / numColumns  )  );   // We don't want to include a `marginLeft` on the first item of a row  const marginLeft = index % numColumns === 0 ? 0 : itemMargin;   // We don't want to include a `marginTop` on the first row of the grid  const marginTop = index lt; numColumns ? 0 : itemMargin;   return renderItem({  ...item,  size,  marginLeft,  marginTop,  });  };   const renderFooter = () =gt; {  if (!isLoading) return null;   return (  lt;View style={globalStyles.listFooter}gt;  lt;Loading /gt;  lt;/Viewgt;  );  };   return (  lt;FlatList  {...props}  data={data}  keyExtractor={keyExtractor}  numColumns={numColumns}  initialNumToRender={initialNumToRender}  renderItem={renderGridItem}  ListFooterComponent={renderFooter}  onEndReachedThreshold={onEndReachedThreshold}  onEndReached={onEndReached}  style={globalStyles.listContainer}  contentContainerStyle={globalStyles.listContainer}  /gt;  ); }  

Кроме того, у меня есть компонент верхнего уровня, который просто отображает мой запомненный компонент внутри моего пользовательского компонента сетки, используя пользовательскую функцию renderItem:

 export default function UserPostsGrid({  listKey,  data = [],  numColumns,  initialNumToRender,  itemMargin,  isLoading,  ListEmptyComponent,  onEndReached,  ...props }) {  const keyExtractor = useCallback(({ id }) =gt; id, []);   const renderItem = useCallback(  ({ item, index, size, marginLeft, marginTop }) =gt; {  const style = {  width: size,  height: size,  marginLeft,  marginTop,  };   return lt;UserPostsGridItem item={item} index={index} style={style} /gt;;  },  []  );   return (  lt;Grid  {...props}  listKey={listKey}  data={data}  keyExtractor={keyExtractor}  numColumns={numColumns}  renderItem={renderItem}  initialNumToRender={initialNumToRender}  isLoading={isLoading}  removeClippedSubviews={false}  ListEmptyComponent={ListEmptyComponent}  itemMargin={itemMargin}  onEndReachedThreshold={0.5}  onEndReached={onEndReached}  /gt;  ); }  

The thing is, that for some reason, my memoized components which are returned from the renderItem function, are magically re-rendered when the data prop changes.

For example, if I add a new element to my current data list, the FlatList will re-render all of its items.

Here is my memoized component:

 function UserPostsGridItem({  item: {  index,  images: [{ uri, thumbnailUri }],  },  style, }) {  const handleOnPress = () =gt; {  // TODO - Push card lists stack  console.log(index);  };   console.log("Rerendering grid item!", uri);   return (  lt;TouchableOpacity activeOpacity={0.5} onPress={handleOnPress} style={style}gt;  lt;Image  uri={uri}  thumbnailUri={thumbnailUri}  imageFadeDuration={200}  thumbnailFadeDuration={100}  withLoading={false}  style={globalStyles.flexContainer}  /gt;  lt;/TouchableOpacitygt;  ); }  function areUserPostsGridItemPropsEqual() {  console.log("Running areEqual"); // lt;---- NOTE THIS!  return true; }  export default memo(UserPostsGridItem, areUserPostsGridItemPropsEqual);  

Как вы можете видеть, мой метод AreEqual возвращает значение true, поэтому я предполагаю, что ошибка в одном из родительских компонентов (Grid или UserPostsGrid).

Есть какие-нибудь идеи о том, что я делаю не так?

Примечание

После изменения реквизита данных (состояние в родительском) из

 data=[{id: "1", ...}]  

Для

 data=[{id: "2", ...}, {id: "1", ...}]  

Я получаю это в консоли:

 "Rerendering grid item! uri..." "Rerendering grid item! uri..."  

ФУНКЦИЯ ARE EQUAL НЕ ВЫПОЛНЯЕТСЯ!!!!!!

Ответ №1:

Хорошо, проблема в том, что numColumns gt; 1.

Из-за виртуализации мои компоненты монтируются заново, а не повторно визуализируются.