Always use static/cached selectors in useSelector

This commit is contained in:
Jamie Kyle 2024-03-15 07:38:26 -07:00 committed by GitHub
parent d85a1d5074
commit 1e275a917c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 105 additions and 70 deletions

View file

@ -10,3 +10,5 @@ export const getHasInitialLoadCompleted = createSelector(
getApp,
({ hasInitialLoadCompleted }) => hasInitialLoadCompleted
);
export const getAppView = createSelector(getApp, ({ appView }) => appView);

View file

@ -208,6 +208,12 @@ export const getTargetedMessage = createSelector(
};
}
);
export const getTargetedMessageSource = createSelector(
getConversations,
(state: ConversationsStateType): string | undefined => {
return state.targetedMessageSource;
}
);
export const getSelectedMessageIds = createSelector(
getConversations,
(state: ConversationsStateType): ReadonlyArray<string> | undefined => {

View file

@ -0,0 +1,17 @@
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { createSelector } from 'reselect';
import type { StateType } from '../reducer';
const getInboxState = (state: StateType) => state.inbox;
export const getInboxEnvelopeTimestamp = createSelector(
getInboxState,
({ envelopeTimestamp }) => envelopeTimestamp
);
export const getInboxFirstEnvelopeTimestamp = createSelector(
getInboxState,
({ firstEnvelopeTimestamp }) => firstEnvelopeTimestamp
);

View file

@ -25,3 +25,12 @@ export const getContactSafetyNumber = createSelector(
contactID: string
): SafetyNumberContactType | void => contacts[contactID]
);
export const getContactSafetyNumberSelector = createSelector(
[getSafetyNumber],
({ contacts }) => {
return (contactId: string) => {
return contacts[contactId];
};
}
);

View file

@ -16,20 +16,20 @@ import {
getIsMainWindowFullScreen,
} from '../selectors/user';
import { hasSelectedStoryData } from '../selectors/stories';
import type { StateType } from '../reducer';
import { useAppActions } from '../ducks/app';
import { useConversationsActions } from '../ducks/conversations';
import { useStoriesActions } from '../ducks/stories';
import { ErrorBoundary } from '../../components/ErrorBoundary';
import { ModalContainer } from '../../components/ModalContainer';
import { SmartInbox } from './Inbox';
import { getAppView } from '../selectors/app';
function renderInbox(): JSX.Element {
return <SmartInbox />;
}
export const SmartApp = memo(function SmartApp() {
const app = useSelector((state: StateType) => state.app);
const appView = useSelector(getAppView);
const { openInbox } = useAppActions();
const { scrollToMessage } = useConversationsActions();
@ -37,7 +37,7 @@ export const SmartApp = memo(function SmartApp() {
return (
<App
{...app}
appView={appView}
isMaximized={useSelector(getIsMainWindowMaximized)}
isFullScreen={useSelector(getIsMainWindowFullScreen)}
osClassName={OS.getClassName()}

View file

@ -13,7 +13,6 @@ import { usePrevious } from '../../hooks/usePrevious';
import { TargetedMessageSource } from '../ducks/conversationsEnums';
import { useConversationsActions } from '../ducks/conversations';
import { useToastActions } from '../ducks/toast';
import type { StateType } from '../reducer';
import { strictAssert } from '../../util/assert';
import { ToastType } from '../../types/Toast';
import { getNavTabsCollapsed } from '../selectors/items';
@ -21,6 +20,11 @@ import { useItemsActions } from '../ducks/items';
import { getHasAnyFailedStorySends } from '../selectors/stories';
import { getHasPendingUpdate } from '../selectors/updates';
import { getOtherTabsUnreadStats } from '../selectors/nav';
import {
getSelectedConversationId,
getTargetedMessage,
getTargetedMessageSource,
} from '../selectors/conversations';
function renderConversationView() {
return <SmartConversationView />;
@ -40,9 +44,9 @@ export const SmartChatsTab = memo(function SmartChatsTab() {
const hasFailedStorySends = useSelector(getHasAnyFailedStorySends);
const hasPendingUpdate = useSelector(getHasPendingUpdate);
const otherTabsUnreadStats = useSelector(getOtherTabsUnreadStats);
const { selectedConversationId, targetedMessage, targetedMessageSource } =
useSelector((state: StateType) => state.conversations);
const selectedConversationId = useSelector(getSelectedConversationId);
const targetedMessage = useSelector(getTargetedMessage);
const targetedMessageSource = useSelector(getTargetedMessageSource);
const {
onConversationClosed,
@ -60,14 +64,14 @@ export const SmartChatsTab = memo(function SmartChatsTab() {
if (selectedConversationId !== lastOpenedConversationId.current) {
lastOpenedConversationId.current = selectedConversationId;
if (selectedConversationId) {
onConversationOpened(selectedConversationId, targetedMessage);
onConversationOpened(selectedConversationId, targetedMessage?.id);
}
} else if (
selectedConversationId &&
targetedMessage &&
targetedMessageSource !== TargetedMessageSource.Focus
) {
scrollToMessage(selectedConversationId, targetedMessage);
scrollToMessage(selectedConversationId, targetedMessage?.id);
}
}, [onConversationOpened, selectedConversationId, scrollToMessage, targetedMessage, targetedMessageSource]);

View file

@ -15,7 +15,6 @@ import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPe
import { imageToBlurHash } from '../../util/imageToBlurHash';
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly';
import { isSignalConversation } from '../../util/isSignalConversation';
import type { StateType } from '../reducer';
import {
getErrorDialogAudioRecorderType,
getRecordingState,
@ -163,15 +162,23 @@ export const SmartCompositionArea = memo(function SmartCompositionArea({
};
}
const quotedMessageProps = useSelector((state: StateType) => {
const ourConversationId = useSelector(getUserConversationId);
const defaultConversationColor = useSelector(getDefaultConversationColor);
const quotedMessageProps = useMemo(() => {
return quotedMessage
? getPropsForQuote(quotedMessage, {
conversationSelector,
ourConversationId: getUserConversationId(state),
defaultConversationColor: getDefaultConversationColor(state),
ourConversationId,
defaultConversationColor,
})
: undefined;
});
}, [
quotedMessage,
conversationSelector,
ourConversationId,
defaultConversationColor,
]);
const { putItem, removeItem } = useItemsActions();

View file

@ -5,7 +5,6 @@ import React, { memo, useMemo } from 'react';
import { useSelector } from 'react-redux';
import { pick } from 'lodash';
import type { ConversationType } from '../ducks/conversations';
import type { StateType } from '../reducer';
import {
ConversationHeader,
OutgoingCallButtonStyle,
@ -19,14 +18,13 @@ import {
isMissingRequiredProfileSharing,
} from '../selectors/conversations';
import { CallMode } from '../../types/Calling';
import { getActiveCall, useCallingActions } from '../ducks/calling';
import { useCallingActions } from '../ducks/calling';
import { isAnybodyElseInGroupCall } from '../ducks/callingHelpers';
import {
getConversationCallMode,
useConversationsActions,
} from '../ducks/conversations';
import { getHasStoriesSelector } from '../selectors/stories2';
import { getOwn } from '../../util/getOwn';
import { getUserACI, getIntl, getTheme } from '../selectors/user';
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly';
import { missingCaseError } from '../../util/missingCaseError';
@ -39,20 +37,21 @@ import { getGroupMemberships } from '../../util/getGroupMemberships';
import { isGroupOrAdhocCallState } from '../../util/isGroupOrAdhocCall';
import { useContactNameData } from '../../components/conversation/ContactName';
import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation';
import { getActiveCallState, getCallSelector } from '../selectors/calling';
export type OwnProps = {
id: string;
};
const getOutgoingCallButtonStyle = (
conversation: ConversationType,
state: StateType
const useOutgoingCallButtonStyle = (
conversation: ConversationType
): OutgoingCallButtonStyle => {
const { calling } = state;
const ourAci = getUserACI(state);
strictAssert(ourAci, 'getOutgoingCallButtonStyle missing our uuid');
const ourAci = useSelector(getUserACI);
const activeCall = useSelector(getActiveCallState);
const callSelector = useSelector(getCallSelector);
strictAssert(ourAci, 'useOutgoingCallButtonStyle missing our uuid');
if (getActiveCall(calling)) {
if (activeCall != null) {
return OutgoingCallButtonStyle.None;
}
@ -64,7 +63,7 @@ const getOutgoingCallButtonStyle = (
return OutgoingCallButtonStyle.Both;
case CallMode.Group:
case CallMode.Adhoc: {
const call = getOwn(calling.callsByConversation, conversation.id);
const call = callSelector(conversation.id);
if (
isGroupOrAdhocCallState(call) &&
isAnybodyElseInGroupCall(call.peekInfo, ourAci)
@ -94,9 +93,7 @@ export const SmartConversationHeader = memo(function SmartConversationHeader({
const badge = badgeSelector(conversation.badges);
const i18n = useSelector(getIntl);
const hasPanelShowing = useSelector(getHasPanelOpen);
const outgoingCallButtonStyle = useSelector((state: StateType) => {
return getOutgoingCallButtonStyle(conversation, state);
});
const outgoingCallButtonStyle = useOutgoingCallButtonStyle(conversation);
const theme = useSelector(getTheme);
const {

View file

@ -3,7 +3,6 @@
import React, { memo, useCallback } from 'react';
import { useSelector } from 'react-redux';
import type { StateType } from '../reducer';
import { ConversationPanel } from './ConversationPanel';
import { ConversationView } from '../../components/conversation/ConversationView';
import { SmartCompositionArea } from './CompositionArea';
@ -17,6 +16,7 @@ import {
} from '../selectors/conversations';
import { useComposerActions } from '../ducks/composer';
import { useConversationsActions } from '../ducks/conversations';
import { isShowingAnyModal } from '../selectors/globalModals';
function renderCompositionArea(conversationId: string) {
return <SmartCompositionArea id={conversationId} />;
@ -48,19 +48,10 @@ export const SmartConversationView = memo(
const { processAttachments } = useComposerActions();
const hasOpenModal = useSelector((state: StateType) => {
return (
state.globalModals.forwardMessagesProps != null ||
state.globalModals.deleteMessagesProps != null ||
state.globalModals.hasConfirmationModal
);
});
const shouldHideConversationView = useSelector((state: StateType) => {
const activePanel = getActivePanel(state);
const isAnimating = getIsPanelAnimating(state);
return activePanel && !isAnimating;
});
const hasOpenModal = useSelector(isShowingAnyModal);
const activePanel = useSelector(getActivePanel);
const isPanelAnimating = useSelector(getIsPanelAnimating);
const shouldHideConversationView = activePanel && !isPanelAnimating;
const onExitSelectMode = useCallback(() => {
toggleSelectMode(false);

View file

@ -1,7 +1,7 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { memo } from 'react';
import React, { memo, useCallback } from 'react';
import { useSelector } from 'react-redux';
import type { StateType } from '../reducer';
import { getIntl } from '../selectors/user';
@ -25,13 +25,17 @@ export const SmartDeleteMessagesModal = memo(
'Cannot render delete messages modal without messages'
);
const { conversationId, messageIds, onDelete } = deleteMessagesProps;
const isMe = useSelector((state: StateType) => {
return getConversationSelector(state)(conversationId).isMe;
});
const conversationSelector = useSelector(getConversationSelector);
const conversation = conversationSelector(conversationId);
const { isMe } = conversation;
const canDeleteForEveryone = useSelector((state: StateType) => {
return canDeleteMessagesForEveryone(state, { messageIds, isMe });
});
const getCanDeleteForEveryone = useCallback(
(state: StateType) => {
return canDeleteMessagesForEveryone(state, { messageIds, isMe });
},
[messageIds, isMe]
);
const canDeleteForEveryone = useSelector(getCanDeleteForEveryone);
const lastSelectedMessage = useSelector(getLastSelectedMessage);
const i18n = useSelector(getIntl);
const { toggleDeleteMessagesModal } = useGlobalModalActions();

View file

@ -3,7 +3,6 @@
import React, { memo } from 'react';
import { useSelector } from 'react-redux';
import type { StateType } from '../reducer';
import { Inbox } from '../../components/Inbox';
import { getIntl } from '../selectors/user';
import { SmartCustomizingPreferredReactionsModal } from './CustomizingPreferredReactionsModal';
@ -15,6 +14,11 @@ import { SmartCallsTab } from './CallsTab';
import { useItemsActions } from '../ducks/items';
import { getNavTabsCollapsed } from '../selectors/items';
import { SmartChatsTab } from './ChatsTab';
import { getHasInitialLoadCompleted } from '../selectors/app';
import {
getInboxEnvelopeTimestamp,
getInboxFirstEnvelopeTimestamp,
} from '../selectors/inbox';
function renderChatsTab() {
return <SmartChatsTab />;
@ -41,17 +45,11 @@ export const SmartInbox = memo(function SmartInbox(): JSX.Element {
const isCustomizingPreferredReactions = useSelector(
getIsCustomizingPreferredReactions
);
const envelopeTimestamp = useSelector(
(state: StateType) => state.inbox.envelopeTimestamp
);
const firstEnvelopeTimestamp = useSelector(
(state: StateType) => state.inbox.firstEnvelopeTimestamp
);
const { hasInitialLoadCompleted } = useSelector(
(state: StateType) => state.app
);
const envelopeTimestamp = useSelector(getInboxEnvelopeTimestamp);
const firstEnvelopeTimestamp = useSelector(getInboxFirstEnvelopeTimestamp);
const hasInitialLoadCompleted = useSelector(getHasInitialLoadCompleted);
const navTabsCollapsed = useSelector(getNavTabsCollapsed);
const { toggleNavTabsCollapse } = useItemsActions();
return (

View file

@ -3,8 +3,7 @@
import React, { memo } from 'react';
import { useSelector } from 'react-redux';
import { SafetyNumberModal } from '../../components/SafetyNumberModal';
import type { StateType } from '../reducer';
import { getContactSafetyNumber } from '../selectors/safetyNumber';
import { getContactSafetyNumberSelector } from '../selectors/safetyNumber';
import { getConversationSelector } from '../selectors/conversations';
import { getIntl } from '../selectors/user';
import { useSafetyNumberActions } from '../ducks/safetyNumber';
@ -20,9 +19,10 @@ export const SmartSafetyNumberModal = memo(function SmartSafetyNumberModal({
const i18n = useSelector(getIntl);
const conversationSelector = useSelector(getConversationSelector);
const contact = conversationSelector(contactID);
const contactSafetyNumber = useSelector((state: StateType) => {
return getContactSafetyNumber(state, { contactID });
});
const contactSafetyNumberSelector = useSelector(
getContactSafetyNumberSelector
);
const contactSafetyNumber = contactSafetyNumberSelector(contactID);
const { generateSafetyNumber, toggleVerified } = useSafetyNumberActions();
const { toggleSafetyNumberModal } = useGlobalModalActions();
return (

View file

@ -4,9 +4,8 @@
import React, { memo } from 'react';
import { useSelector } from 'react-redux';
import { SafetyNumberViewer } from '../../components/SafetyNumberViewer';
import type { StateType } from '../reducer';
import type { SafetyNumberProps } from '../../components/SafetyNumberChangeDialog';
import { getContactSafetyNumber } from '../selectors/safetyNumber';
import { getContactSafetyNumberSelector } from '../selectors/safetyNumber';
import { getConversationSelector } from '../selectors/conversations';
import { getIntl } from '../selectors/user';
import { useSafetyNumberActions } from '../ducks/safetyNumber';
@ -16,9 +15,10 @@ export const SmartSafetyNumberViewer = memo(function SmartSafetyNumberViewer({
onClose,
}: SafetyNumberProps) {
const i18n = useSelector(getIntl);
const safetyNumberContact = useSelector((state: StateType) => {
return getContactSafetyNumber(state, { contactID });
});
const contactSafetyNumberSelector = useSelector(
getContactSafetyNumberSelector
);
const safetyNumberContact = contactSafetyNumberSelector(contactID);
const conversationSelector = useSelector(getConversationSelector);
const contact = conversationSelector(contactID);