Skip to content

Commit

Permalink
Separate ReportScreenContext to prevent cyclic re-renders
Browse files Browse the repository at this point in the history
  • Loading branch information
ospfranco committed Aug 25, 2023
1 parent fd407e2 commit 3be5e69
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 110 deletions.
4 changes: 2 additions & 2 deletions src/components/Reactions/ReportActionItemEmojiReactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import EmojiReactionsPropTypes from './EmojiReactionsPropTypes';
import Tooltip from '../Tooltip';
import ReactionTooltipContent from './ReactionTooltipContent';
import * as EmojiUtils from '../../libs/EmojiUtils';
import ReportScreenContext from '../../pages/home/ReportScreenContext';
import {ReactionListContext} from '../../pages/home/ReportScreenContext';

const propTypes = {
emojiReactions: EmojiReactionsPropTypes,
Expand Down Expand Up @@ -41,7 +41,7 @@ const defaultProps = {
};

function ReportActionItemEmojiReactions(props) {
const {reactionListRef} = useContext(ReportScreenContext);
const reactionListRef = useContext(ReactionListContext);
const popoverReactionListAnchor = useRef(null);
let totalReactionCount = 0;

Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useReportScrollManager/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {useContext, useCallback} from 'react';
import ReportScreenContext from '../../pages/home/ReportScreenContext';
import {ActionListContext} from '../../pages/home/ReportScreenContext';

function useReportScrollManager() {
const {flatListRef} = useContext(ReportScreenContext);
const flatListRef = useContext(ActionListContext);

/**
* Scroll to the provided index. On non-native implementations we do not want to scroll when we are scrolling because
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useReportScrollManager/index.native.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {useContext, useCallback} from 'react';
import ReportScreenContext from '../../pages/home/ReportScreenContext';
import {ActionListContext} from '../../pages/home/ReportScreenContext';

function useReportScrollManager() {
const {flatListRef} = useContext(ReportScreenContext);
const flatListRef = useContext(ActionListContext);

/**
* Scroll to the provided index.
Expand Down
188 changes: 92 additions & 96 deletions src/pages/home/ReportScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import getIsReportFullyVisible from '../../libs/getIsReportFullyVisible';
import MoneyRequestHeader from '../../components/MoneyRequestHeader';
import MoneyReportHeader from '../../components/MoneyReportHeader';
import * as ComposerActions from '../../libs/actions/Composer';
import ReportScreenContext from './ReportScreenContext';
import {ActionListContext, ReactionListContext} from './ReportScreenContext';
import TaskHeaderActionButton from '../../components/TaskHeaderActionButton';
import DragAndDropProvider from '../../components/DragAndDrop/Provider';

Expand Down Expand Up @@ -276,111 +276,107 @@ class ReportScreen extends React.Component {
}

return (
<ReportScreenContext.Provider
value={{
flatListRef: this.flatListRef,
reactionListRef: this.reactionListRef,
}}
>
<ScreenWrapper
style={screenWrapperStyle}
shouldEnableKeyboardAvoidingView={isTopMostReportId}
>
<FullPageNotFoundView
shouldShow={(!this.props.report.reportID && !this.props.report.isLoadingReportActions && !isLoading) || shouldHideReport}
subtitleKey="notFound.noAccess"
shouldShowCloseButton={false}
shouldShowBackButton={this.props.isSmallScreenWidth}
onBackButtonPress={Navigation.goBack}
shouldShowLink={false}
<ActionListContext.Provider value={this.flatListRef}>
<ReactionListContext.Provider value={this.reactionListRef}>
<ScreenWrapper
style={screenWrapperStyle}
shouldEnableKeyboardAvoidingView={isTopMostReportId}
>
<OfflineWithFeedback
pendingAction={addWorkspaceRoomOrChatPendingAction}
errors={addWorkspaceRoomOrChatErrors}
shouldShowErrorMessages={false}
needsOffscreenAlphaCompositing
<FullPageNotFoundView
shouldShow={(!this.props.report.reportID && !this.props.report.isLoadingReportActions && !isLoading) || shouldHideReport}
subtitleKey="notFound.noAccess"
shouldShowCloseButton={false}
shouldShowBackButton={this.props.isSmallScreenWidth}
onBackButtonPress={Navigation.goBack}
shouldShowLink={false}
>
{headerView}
{ReportUtils.isTaskReport(this.props.report) && this.props.isSmallScreenWidth && ReportUtils.isOpenTaskReport(this.props.report) && (
<View style={[styles.borderBottom]}>
<View style={[styles.appBG, styles.pl0]}>
<View style={[styles.ph5, styles.pb3]}>
<TaskHeaderActionButton report={this.props.report} />
<OfflineWithFeedback
pendingAction={addWorkspaceRoomOrChatPendingAction}
errors={addWorkspaceRoomOrChatErrors}
shouldShowErrorMessages={false}
needsOffscreenAlphaCompositing
>
{headerView}
{ReportUtils.isTaskReport(this.props.report) && this.props.isSmallScreenWidth && ReportUtils.isOpenTaskReport(this.props.report) && (
<View style={[styles.borderBottom]}>
<View style={[styles.appBG, styles.pl0]}>
<View style={[styles.ph5, styles.pb3]}>
<TaskHeaderActionButton report={this.props.report} />
</View>
</View>
</View>
</View>
)}
</OfflineWithFeedback>
{Boolean(this.props.accountManagerReportID) && ReportUtils.isConciergeChatReport(this.props.report) && this.state.isBannerVisible && (
<Banner
containerStyles={[styles.mh4, styles.mt4, styles.p4, styles.bgDark]}
textStyles={[styles.colorReversed]}
text={this.props.translate('reportActionsView.chatWithAccountManager')}
onClose={this.dismissBanner}
onPress={this.chatWithAccountManager}
shouldShowCloseButton
/>
)}
<DragAndDropProvider isDisabled={!this.isReportReadyForDisplay()}>
<View
style={[styles.flex1, styles.justifyContentEnd, styles.overflowHidden]}
onLayout={(event) => {
// Rounding this value for comparison because they can look like this: 411.9999694824219
const skeletonViewContainerHeight = Math.round(event.nativeEvent.layout.height);

// Only set state when the height changes to avoid unnecessary renders
if (reportActionsListViewHeight === skeletonViewContainerHeight) return;

// The height can be 0 if the component unmounts - we are not interested in this value and want to know how much space it
// takes up so we can set the skeleton view container height.
if (skeletonViewContainerHeight === 0) {
return;
}
reportActionsListViewHeight = skeletonViewContainerHeight;
this.setState({skeletonViewContainerHeight});
}}
>
{this.isReportReadyForDisplay() && !isLoadingInitialReportActions && !isLoading && (
<ReportActionsView
reportActions={this.props.reportActions}
report={this.props.report}
isComposerFullSize={this.props.isComposerFullSize}
parentViewHeight={this.state.skeletonViewContainerHeight}
policy={policy}
/>
)}

{/* Note: The report should be allowed to mount even if the initial report actions are not loaded. If we prevent rendering the report while they are loading then
we'll unnecessarily unmount the ReportActionsView which will clear the new marker lines initial state. */}
{(!this.isReportReadyForDisplay() || isLoadingInitialReportActions || isLoading) && (
<ReportActionsSkeletonView containerHeight={this.state.skeletonViewContainerHeight} />
)}

{this.isReportReadyForDisplay() && (
<>
<ReportFooter
pendingAction={addWorkspaceRoomOrChatPendingAction}
isOffline={this.props.network.isOffline}
</OfflineWithFeedback>
{Boolean(this.props.accountManagerReportID) && ReportUtils.isConciergeChatReport(this.props.report) && this.state.isBannerVisible && (
<Banner
containerStyles={[styles.mh4, styles.mt4, styles.p4, styles.bgDark]}
textStyles={[styles.colorReversed]}
text={this.props.translate('reportActionsView.chatWithAccountManager')}
onClose={this.dismissBanner}
onPress={this.chatWithAccountManager}
shouldShowCloseButton
/>
)}
<DragAndDropProvider isDisabled={!this.isReportReadyForDisplay()}>
<View
style={[styles.flex1, styles.justifyContentEnd, styles.overflowHidden]}
onLayout={(event) => {
// Rounding this value for comparison because they can look like this: 411.9999694824219
const skeletonViewContainerHeight = Math.round(event.nativeEvent.layout.height);

// Only set state when the height changes to avoid unnecessary renders
if (reportActionsListViewHeight === skeletonViewContainerHeight) return;

// The height can be 0 if the component unmounts - we are not interested in this value and want to know how much space it
// takes up so we can set the skeleton view container height.
if (skeletonViewContainerHeight === 0) {
return;
}
reportActionsListViewHeight = skeletonViewContainerHeight;
this.setState({skeletonViewContainerHeight});
}}
>
{this.isReportReadyForDisplay() && !isLoadingInitialReportActions && !isLoading && (
<ReportActionsView
reportActions={this.props.reportActions}
report={this.props.report}
isComposerFullSize={this.props.isComposerFullSize}
onSubmitComment={this.onSubmitComment}
policies={this.props.policies}
policy={policy}
/>
</>
)}
)}

{!this.isReportReadyForDisplay() && (
<ReportFooter
shouldDisableCompose
isOffline={this.props.network.isOffline}
/>
)}
</View>
</DragAndDropProvider>
</FullPageNotFoundView>
</ScreenWrapper>
</ReportScreenContext.Provider>
{/* Note: The report should be allowed to mount even if the initial report actions are not loaded. If we prevent rendering the report while they are loading then
we'll unnecessarily unmount the ReportActionsView which will clear the new marker lines initial state. */}
{(!this.isReportReadyForDisplay() || isLoadingInitialReportActions || isLoading) && (
<ReportActionsSkeletonView containerHeight={this.state.skeletonViewContainerHeight} />
)}

{this.isReportReadyForDisplay() && (
<>
<ReportFooter
pendingAction={addWorkspaceRoomOrChatPendingAction}
isOffline={this.props.network.isOffline}
reportActions={this.props.reportActions}
report={this.props.report}
isComposerFullSize={this.props.isComposerFullSize}
onSubmitComment={this.onSubmitComment}
policies={this.props.policies}
/>
</>
)}

{!this.isReportReadyForDisplay() && (
<ReportFooter
shouldDisableCompose
isOffline={this.props.network.isOffline}
/>
)}
</View>
</DragAndDropProvider>
</FullPageNotFoundView>
</ScreenWrapper>
</ReactionListContext.Provider>
</ActionListContext.Provider>
);
}
}
Expand Down
9 changes: 7 additions & 2 deletions src/pages/home/ReportScreenContext.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import {createContext} from 'react';

const ReportScreenContext = createContext();
export default ReportScreenContext;
const ActionListContext = createContext();
const ReactionListContext = createContext();

export {
ActionListContext,
ReactionListContext
}
4 changes: 2 additions & 2 deletions src/pages/home/report/ReportActionItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ import * as PersonalDetailsUtils from '../../../libs/PersonalDetailsUtils';
import ReportActionItemBasicMessage from './ReportActionItemBasicMessage';
import * as store from '../../../libs/actions/ReimbursementAccount/store';
import * as BankAccounts from '../../../libs/actions/BankAccounts';
import ReportScreenContext from '../ReportScreenContext';
import { ReactionListContext } from '../ReportScreenContext';
import Permissions from '../../../libs/Permissions';

const propTypes = {
Expand Down Expand Up @@ -127,7 +127,7 @@ function ReportActionItem(props) {
const [isContextMenuActive, setIsContextMenuActive] = useState(ReportActionContextMenu.isActiveReportAction(props.action.reportActionID));
const [isHidden, setIsHidden] = useState(false);
const [moderationDecision, setModerationDecision] = useState(CONST.MODERATION.MODERATOR_DECISION_APPROVED);
const {reactionListRef} = useContext(ReportScreenContext);
const reactionListRef = useContext(ReactionListContext);
const textInputRef = useRef();
const popoverAnchorRef = useRef();
const downloadedPreviews = useRef([]);
Expand Down
7 changes: 3 additions & 4 deletions src/pages/home/report/ReportActionsView.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import * as ReportActionsUtils from '../../../libs/ReportActionsUtils';
import reportPropTypes from '../../reportPropTypes';
import PopoverReactionList from './ReactionList/PopoverReactionList';
import getIsReportFullyVisible from '../../../libs/getIsReportFullyVisible';
import ReportScreenContext from '../ReportScreenContext';
import { ReactionListContext } from '../ReportScreenContext';

const propTypes = {
/** The report currently being looked at */
Expand Down Expand Up @@ -54,10 +54,9 @@ const defaultProps = {
};

function ReportActionsView(props) {
const context = useContext(ReportScreenContext);

useCopySelectionHelper();

const reactionListRef = useContext(ReactionListContext)
const didLayout = useRef(false);
const didSubscribeToReportTypingEvents = useRef(false);
const hasCachedActions = useRef(_.size(props.reportActions) > 0);
Expand Down Expand Up @@ -189,7 +188,7 @@ function ReportActionsView(props) {
loadMoreChats={loadMoreChats}
policy={props.policy}
/>
<PopoverReactionList ref={context.reactionListRef} />
<PopoverReactionList ref={reactionListRef} />
</>
);
}
Expand Down

0 comments on commit 3be5e69

Please sign in to comment.