Search code examples
javascriptreact-nativeflatlistmemo

You have a large list that is slow to update error after memoizing renderItem function


I've tried every solution on StackOverflow, a couple of other webpages and various chatbots. They all give me several solutions, including: wrapping my renderItem function in a memoization, using a pureComponent class instead of a function component and useCallback.

No matter what I use, I'm still getting the same error. It causes the UI to freeze up, so I can't just ignore it! What's even more frustrating, is that I wrapped the renderItem function in a memo for another list with a similar renderItem function and data structure, and it worked!

I've tried the following code:

const BleSections = (params) => {
    if (!params.device) {
        return [];
    }

    const { sections: deviceSections } = params.device;

    return [
        {
            title: "Address",
            data: [
                { label: "MAC", value: params.device.bleDeviceAddress },
            ],
        },
        {
            title: "Temperature",
            data: [
                { label: "Cloud ID", value: deviceSections?.Temperature?.cloud_id },
                { label: "Local ID", value: deviceSections?.Temperature?.local_id },
            ],
        },
        {
            title: "Humidity",
            data: [
                { label: "Cloud ID", value: deviceSections?.Humidity?.cloud_id },
                { label: "Local ID", value: deviceSections?.Humidity?.local_id },
            ],
        },
        {
            title: "Panic",
            data: [
                { label: "Cloud ID", value: deviceSections?.Panic?.cloud_id },
                { label: "Local ID", value: deviceSections?.Panic?.local_id },
            ],
        },
    ];
};

export default BleSections;
const sections = BleSections({
    device: Object.values(bleDevices).find(device => bleDevice &&
    device.bleDeviceAddress === bleDevice.id)
});
import React, { memo } from "react";
import { View, Text } from "react-native";

import ScanStyle from "../Styles/ScanStyle.js";

import BleTextRender from "./BleTextRender.js";

const BleSectionRender = ({ item: section }) => {
    return (
        <View style={ScanStyle.subSectionContainer}>
            <Text style={ScanStyle.subSectionTitle}>
                {section.title}
            </Text>
            {section.data.map((item, itemIndex) => (
                <View key={itemIndex}>
                    <BleTextRender
                        label={item.label}
                        value={item.value}
                    />
                </View>
            ))}
        </View>
    );
};

export default memo(BleSectionRender);
import React, { memo } from "react";
import { View, Text } from "react-native";

import ScanStyle from "../Styles/ScanStyle";

const BleTextRender = ({ label, value }) => {
    return (
        <View style={ScanStyle.textContainer}>
            <Text style={ScanStyle.textLabel}>
                {label}:
            </Text>
            <Text style={ScanStyle.textValue}>
                {value ? value : "Not set"}
            </Text>
        </View>
    );
};

export default memo(BleTextRender);
<FlatList                        
    data={sections}
    renderItem={BleSectionRender} 
    keyExtractor={(item, index) => index.toString()}                                 
    contentContainerStyle={ScanStyle.content}
/>

Solution

  • You need to be careful with React Memo, because using it incorrectly will lead to worse performance, not better.

    Before using memoization, you should make sure that the props you pass to the memoized component are also memoized.

    For example, in your BleSectionRender component there is a prop item, it is an object, and with each re-render the reference to the object changes and memoization will not work and will only harm. You can improve this by passing the parameters, title and data, as props separately (should be memoized in the place where you call BleSectionRender with the useMemo hook).

    FlatList component

    you pass the sections prop there, wrap

    const sections = BleSections({
    device: Object.values(bleDevices).find(device => bleDevice &&
    device.bleDeviceAddress === bleDevice.id)
    }); 
    

    in useMemo. If you have sections in this form in the component body - in this case, your sections are updated with each re-render and find is called each time (find is not the best solution when working with large lists, it is quite heavy, I can suggest working with it as with objects and not an array. For example: if you make bleDevices an object in which the key is id. bleDevices[bleDevice?.id] - you will make a selection this way and it will take up fewer resources when working with large lists)

    If you show more code related to these components, I can give a more detailed recommendation.