Skip to content

Commit

Permalink
Merge pull request #11907 from margelo/perf/memoize-sidebar
Browse files Browse the repository at this point in the history
perf: memo SidebarLinks
  • Loading branch information
mountiny authored Oct 31, 2022
2 parents 6522435 + 74820f9 commit 50e8901
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 200 deletions.
17 changes: 17 additions & 0 deletions src/components/ScreenWrapper/BaseScreenWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,23 @@ class BaseScreenWrapper extends React.Component {
});
}

/**
* We explicitly want to ignore if props.modal changes, and only want to rerender if
* any of the other props **used for the rendering output** is changed.
* @param {Object} nextProps
* @param {Object} nextState
* @returns {boolean}
*/
shouldComponentUpdate(nextProps, nextState) {
return this.state !== nextState
|| this.props.children !== nextProps.children
|| this.props.network.isOffline !== nextProps.network.isOffline
|| this.props.includePaddingBottom !== nextProps.includePaddingBottom
|| this.props.includePaddingTop !== nextProps.includePaddingTop
|| this.props.isSmallScreenWidth !== nextProps.isSmallScreenWidth
|| this.props.keyboardAvoidingViewBehavior !== nextProps.keyboardAvoidingViewBehavior;
}

componentWillUnmount() {
if (this.unsubscribeEscapeKey) {
this.unsubscribeEscapeKey();
Expand Down
146 changes: 17 additions & 129 deletions src/pages/home/sidebar/SidebarScreen/BaseSidebarScreen.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,31 @@
import lodashGet from 'lodash/get';
import _ from 'underscore';
import React, {Component} from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import styles from '../../../../styles/styles';
import SidebarLinks from '../SidebarLinks';
import PopoverMenu from '../../../../components/PopoverMenu';
import FloatingActionButton from '../../../../components/FloatingActionButton';
import ScreenWrapper from '../../../../components/ScreenWrapper';
import Navigation from '../../../../libs/Navigation/Navigation';
import ROUTES from '../../../../ROUTES';
import Timing from '../../../../libs/actions/Timing';
import CONST from '../../../../CONST';
import * as Expensicons from '../../../../components/Icon/Expensicons';
import Permissions from '../../../../libs/Permissions';
import * as Policy from '../../../../libs/actions/Policy';
import Performance from '../../../../libs/Performance';
import * as Welcome from '../../../../libs/actions/Welcome';
import {sidebarPropTypes, sidebarDefaultProps} from './sidebarPropTypes';
import withDrawerState from '../../../../components/withDrawerState';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../../../components/withWindowDimensions';
import compose from '../../../../libs/compose';
import sidebarPropTypes from './sidebarPropTypes';

const propTypes = {

/** Callback function when the menu is shown */
onShowCreateMenu: PropTypes.func,

/** Callback function before the menu is hidden */
onHideCreateMenu: PropTypes.func,

/** reportID in the current navigation state */
reportIDFromRoute: PropTypes.string,

...sidebarPropTypes,
};
const defaultProps = {
onHideCreateMenu: () => {},
onShowCreateMenu: () => {},
...sidebarDefaultProps,
...windowDimensionsPropTypes,
};

class BaseSidebarScreen extends Component {
constructor(props) {
super(props);

this.hideCreateMenu = this.hideCreateMenu.bind(this);
this.startTimer = this.startTimer.bind(this);
this.navigateToSettings = this.navigateToSettings.bind(this);
this.showCreateMenu = this.showCreateMenu.bind(this);

this.state = {
isCreateMenuActive: false,
};
}

componentDidMount() {
Expand All @@ -61,35 +36,13 @@ class BaseSidebarScreen extends Component {
Welcome.show({routes, showCreateMenu: this.showCreateMenu});
}

/**
* Method called when we click the floating action button
*/
showCreateMenu() {
this.setState({
isCreateMenuActive: true,
});
this.props.onShowCreateMenu();
}

/**
* Method called when avatar is clicked
*/
navigateToSettings() {
Navigation.navigate(ROUTES.SETTINGS);
}

/**
* Method called either when:
* Pressing the floating action button to open the CreateMenu modal
* Selecting an item on CreateMenu or closing it by clicking outside of the modal component
*/
hideCreateMenu() {
this.props.onHideCreateMenu();
this.setState({
isCreateMenuActive: false,
});
}

/**
* Method called when a pinned chat is selected.
*/
Expand All @@ -99,96 +52,31 @@ class BaseSidebarScreen extends Component {
}

render() {
// Workspaces are policies with type === 'free'
const workspaces = _.filter(this.props.allPolicies, policy => policy && policy.type === CONST.POLICY.TYPE.FREE);
return (
<ScreenWrapper
includePaddingBottom={false}
style={[styles.sidebar]}
>
{({insets}) => (
<>
<View style={[styles.flex1]}>
<SidebarLinks
onLinkClick={this.startTimer}
insets={insets}
onAvatarClick={this.navigateToSettings}
isSmallScreenWidth={this.props.isSmallScreenWidth}
isDrawerOpen={this.props.isDrawerOpen}
reportIDFromRoute={this.props.reportIDFromRoute}
/>
<FloatingActionButton
accessibilityLabel={this.props.translate('sidebarScreen.fabNewChat')}
accessibilityRole="button"
isActive={this.state.isCreateMenuActive}
onPress={this.showCreateMenu}
/>
</View>
<PopoverMenu
onClose={this.hideCreateMenu}
isVisible={this.state.isCreateMenuActive}
anchorPosition={styles.createMenuPositionSidebar}
onItemSelected={this.hideCreateMenu}
fromSidebarMediumScreen={!this.props.isSmallScreenWidth}
menuItems={[
{
icon: Expensicons.ChatBubble,
text: this.props.translate('sidebarScreen.newChat'),
onSelected: () => Navigation.navigate(ROUTES.NEW_CHAT),
},
{
icon: Expensicons.Users,
text: this.props.translate('sidebarScreen.newGroup'),
onSelected: () => Navigation.navigate(ROUTES.NEW_GROUP),
},
...(Permissions.canUsePolicyRooms(this.props.betas) && workspaces.length ? [
{
icon: Expensicons.Hashtag,
text: this.props.translate('sidebarScreen.newRoom'),
onSelected: () => Navigation.navigate(ROUTES.WORKSPACE_NEW_ROOM),
},
] : []),
...(Permissions.canUseIOUSend(this.props.betas) ? [
{
icon: Expensicons.Send,
text: this.props.translate('iou.sendMoney'),
onSelected: () => Navigation.navigate(ROUTES.IOU_SEND),
},
] : []),
...(Permissions.canUseIOU(this.props.betas) ? [
{
icon: Expensicons.MoneyCircle,
text: this.props.translate('iou.requestMoney'),
onSelected: () => Navigation.navigate(ROUTES.IOU_REQUEST),
},
] : []),
...(Permissions.canUseIOU(this.props.betas) ? [
{
icon: Expensicons.Receipt,
text: this.props.translate('iou.splitBill'),
onSelected: () => Navigation.navigate(ROUTES.IOU_BILL),
},
] : []),
...(!Policy.isAdminOfFreePolicy(this.props.allPolicies) ? [
{
icon: Expensicons.NewWorkspace,
iconWidth: 46,
iconHeight: 40,
text: this.props.translate('workspace.new.newWorkspace'),
description: this.props.translate('workspace.new.getTheExpensifyCardAndMore'),
onSelected: () => Policy.createWorkspace(),
},
] : []),
]}
<View style={[styles.flex1]}>
<SidebarLinks
onLinkClick={this.startTimer}
insets={insets}
onAvatarClick={this.navigateToSettings}
isSmallScreenWidth={this.props.isSmallScreenWidth}
isDrawerOpen={this.props.isDrawerOpen}
reportIDFromRoute={this.props.reportIDFromRoute}
/>
</>
</View>
)}
</ScreenWrapper>
);
}
}

BaseSidebarScreen.propTypes = propTypes;
BaseSidebarScreen.defaultProps = defaultProps;

export default withDrawerState(BaseSidebarScreen);
export default compose(
withWindowDimensions,
withDrawerState,
)(BaseSidebarScreen);
Loading

0 comments on commit 50e8901

Please sign in to comment.