javascriptreactjsreact-nativererenderreact-native-calendars

How to prevent re-rendering in react-native-calendars


Usecase: at the top level, my app has a tab navigator with a Calendar tab and a Camera tab. In the Calendar tab, it displays an infinitely scrollable CalendarList using react-native-calendars. The app state maintains an object with date strings as keys and sorted arrays of images as values. The calendar's parent view passes it a memoized object containing the last image for each date via context:

const latestImagesByDateString = useMemo(
    () => mapLatestImagesByDateString(imagesByDateString),
    [imagesByDateString]
)

// ...

<ImageContext.Provider value={latestImagesByDateString}>
    <Stack.Screen
        name="CalendarView"
        component={CalendarView}
                            
    />
</ImageContext.Provider>

The CalendarView renders a component for each date, which may have an image:

const getDayComponent = useCallback(
    ({ date, state }) => (
        <CalendarDayView
            imageUri={latestImagesByDateString[date.dateString]?.uri}
            imageId={latestImagesByDateString[date.dateString]?.id}
            isToday={state === 'today'}
            dateString={date.dateString}
            day={date.day}
         />
     ),
     [latestImagesByDateString]
)

// ...

return (
   <CalendarList
       dayComponent={getDayComponent}
   />
)

The Calendar tab has a stack navigator which renders a detail view for a given day when it gets pressed. This is all working as intended.

However, if I navigate to the Camera tab, the user can take a photo, which they can then save or discard. If they save the photo, I update app state with the new photo, then navigate back to the Calendar tab's detail view for that day, showing the new photo.

This also works as intended, except: when I save the photo and navigate to the appropriate detail view, the Calendar will re-render every date in its grid before the detail view gets displayed. This causes a long delay between pressing the "Save photo" button and the detail view being rendered onscreen. When a user saves a new photo, all of the dayComponents except one should be the same as on the previous render. I can't figure out why all the other dayComponents are re-rendering in this case. How can I prevent that from happening?


Solution

  • Update: I solved it. I found the function in the react-native-calendars library where the Day components were being compared for equality and logged the prop name when they weren't equal — this showed me that the dayComponent prop was always a different function when app state got updated. My CalendarDayView component was wrapped in React.memo, but when I added an equality function there, it wasn't being called.

    So instead of using a getDayComponent function, I created a new component called CalendarDayViewWrapper, which I pass to the dayComponent prop of CalendarList. The wrapper component's props are date and state, and it determines the correct imageUri etc before returning the memoized CalendarDayView component. Now the unnecessary re-renders are gone!