-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Fix: Add hasOnceLoadedReportActions guard to all readNewestAction call sites #81462
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,9 @@ type Args = { | |
|
|
||
| /** Callback to call on every scroll event */ | ||
| onTrackScrolling: (event: NativeSyntheticEvent<NativeScrollEvent>) => void; | ||
|
|
||
| /** Whether the report actions have been loaded at least once */ | ||
| hasOnceLoadedReportActions: boolean; | ||
| }; | ||
|
|
||
| export default function useReportUnreadMessageScrollTracking({ | ||
|
|
@@ -32,14 +35,16 @@ export default function useReportUnreadMessageScrollTracking({ | |
| onTrackScrolling, | ||
| unreadMarkerReportActionIndex, | ||
| isInverted, | ||
| hasOnceLoadedReportActions, | ||
| }: Args) { | ||
| const [isFloatingMessageCounterVisible, setIsFloatingMessageCounterVisible] = useState(false); | ||
| const isFocused = useIsFocused(); | ||
| const ref = useRef<{previousViewableItems: ViewToken[]; reportID: string; unreadMarkerReportActionIndex: number; isFocused: boolean}>({ | ||
| const ref = useRef<{previousViewableItems: ViewToken[]; reportID: string; unreadMarkerReportActionIndex: number; isFocused: boolean; hasOnceLoadedReportActions: boolean}>({ | ||
| reportID, | ||
| unreadMarkerReportActionIndex, | ||
| previousViewableItems: [], | ||
| isFocused: true, | ||
| hasOnceLoadedReportActions: false, | ||
| }); | ||
| // We want to save the updated value on ref to use it in onViewableItemsChanged | ||
| // because FlatList requires the callback to be stable and we cannot add a dependency on the useCallback. | ||
|
|
@@ -52,6 +57,10 @@ export default function useReportUnreadMessageScrollTracking({ | |
| ref.current.isFocused = isFocused; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❌ PERF-6 (docs)The Suggested fix: Eliminate the // In the onViewableItemsChanged callback:
if (unreadActionVisible && readActionSkippedRef.current && hasOnceLoadedReportActions) {
readActionSkippedRef.current = false;
readNewestAction(ref.current.reportID);
}Since Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the review! However, I believe the current implementation is correct and follows the established pattern in this file. The // We want to save the updated value on ref to use it in onViewableItemsChanged
// because FlatList requires the callback to be stable and we cannot add a dependency on the useCallback.Because the callback never re-creates, using This same pattern is already used in this file for:
The new |
||
| }, [isFocused]); | ||
|
|
||
| useEffect(() => { | ||
| ref.current.hasOnceLoadedReportActions = hasOnceLoadedReportActions; | ||
| }, [hasOnceLoadedReportActions]); | ||
|
|
||
| /** | ||
| * On every scroll event we want to: | ||
| * Show/hide the latest message pill when user is scrolling back/forth in the history of messages. | ||
|
|
@@ -110,7 +119,8 @@ export default function useReportUnreadMessageScrollTracking({ | |
| } | ||
|
|
||
| // if we're scrolled closer than the offset and read action has been skipped then mark message as read | ||
| if (unreadActionVisible && readActionSkippedRef.current) { | ||
| // Do not try to mark the report as read if the report has not been loaded and shared with the user | ||
| if (unreadActionVisible && readActionSkippedRef.current && ref.current.hasOnceLoadedReportActions) { | ||
| // eslint-disable-next-line no-param-reassign | ||
| readActionSkippedRef.current = false; | ||
| readNewestAction(ref.current.reportID); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
hasOnceLoadedReportActionsis alreadytrueon first render, the ref is still initialized tofalse, so the initialonViewableItemsChangedcall can skipreadNewestActioneven though the report is fully loaded. If the viewable items don’t change afterward (e.g., user opens the report and doesn’t scroll), the skipped read may never be retried, leaving the report unread until another scroll occurs. Initializing the ref with the current prop value avoids this race on first render.Useful? React with 👍 / 👎.