From d6e7e669440d47685842d3e1944db8368dc57cd4 Mon Sep 17 00:00:00 2001 From: Ashoat Tevosyan Date: Tue, 17 Oct 2023 13:25:52 -0400 Subject: [PATCH] [lib] Drill allAtOnce from useChatMentionCandidatesObjAndUtils to useENSNames Summary: This solves [ENG-5274](https://linear.app/comm/issue/ENG-5274/ens-resolution-for-chat-mentioning-causes-too-many-react-rerenders). Additional context about `allAtOnce` in D9515. Depends on D9466 Test Plan: I compared performance of the following two scenarios: 1. `master` + revert of D9465 + D9466 2. `master` + revert of D9465 + D9466 + this diff While I did perceive a slight regression in TTI on the inbox by about a second, I think it's okay to land this stack (including @tomek's work) after this diff is accepted. More details on next steps [here](https://linear.app/comm/issue/ENG-5274/ens-resolution-for-chat-mentioning-causes-too-many-react-rerenders#comment-a7405e2f). Reviewers: tomek, atul Reviewed By: atul Subscribers: wyilio, tomek Differential Revision: https://phab.comm.dev/D9523 --- lib/components/chat-mention-provider.react.js | 11 ++++++++++- lib/utils/entity-helpers.js | 16 +++++++++++----- lib/utils/entity-text.js | 9 ++++++--- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/lib/components/chat-mention-provider.react.js b/lib/components/chat-mention-provider.react.js index 301c8c987c..2daededbdf 100644 --- a/lib/components/chat-mention-provider.react.js +++ b/lib/components/chat-mention-provider.react.js @@ -176,13 +176,22 @@ function getChatMentionCandidates(threadInfos: { }; } +// Without allAtOnce, useChatMentionCandidatesObjAndUtils is very expensive. +// useResolvedThreadInfosObj would trigger its recalculation for each ENS name +// as it streams in, but we would prefer to trigger its recaculation just once +// for every update of the underlying Redux data. +const useResolvedThreadInfosObjOptions = { allAtOnce: true }; + function useChatMentionCandidatesObjAndUtils(): { chatMentionCandidatesObj: ChatMentionCandidatesObj, resolvedThreadInfos: ChatMentionCandidates, communityThreadIDForGenesisThreads: { +[id: string]: string }, } { const threadInfos = useSelector(threadInfoSelector); - const resolvedThreadInfos = useResolvedThreadInfosObj(threadInfos); + const resolvedThreadInfos = useResolvedThreadInfosObj( + threadInfos, + useResolvedThreadInfosObjOptions, + ); const { chatMentionCandidatesObj, communityThreadIDForGenesisThreads } = React.useMemo( () => getChatMentionCandidates(resolvedThreadInfos), diff --git a/lib/utils/entity-helpers.js b/lib/utils/entity-helpers.js index c578da5cfd..4fa733ced0 100644 --- a/lib/utils/entity-helpers.js +++ b/lib/utils/entity-helpers.js @@ -8,17 +8,19 @@ import { useENSNamesForEntityText, entityTextToRawString, } from './entity-text.js'; +import type { UseENSNamesOptions } from '../hooks/ens-cache.js'; import type { ThreadInfo, ResolvedThreadInfo } from '../types/thread-types.js'; import { values } from '../utils/objects.js'; function useResolvedThreadInfos( threadInfos: $ReadOnlyArray, + options?: ?UseENSNamesOptions, ): $ReadOnlyArray { const entityText = React.useMemo( () => threadInfos.map(threadInfo => threadInfo.uiName), [threadInfos], ); - const withENSNames = useENSNamesForEntityText(entityText); + const withENSNames = useENSNamesForEntityText(entityText, options); invariant( withENSNames, 'useENSNamesForEntityText only returns falsey when passed falsey', @@ -76,14 +78,18 @@ function useResolvedOptionalThreadInfos( }, [threadInfos, withENSNames]); } -function useResolvedThreadInfosObj(threadInfosObj: { - +[id: string]: ThreadInfo, -}): { +[id: string]: ResolvedThreadInfo } { +function useResolvedThreadInfosObj( + threadInfosObj: { +[id: string]: ThreadInfo }, + options?: ?UseENSNamesOptions, +): { +[id: string]: ResolvedThreadInfo } { const threadInfosArray = React.useMemo( () => values(threadInfosObj), [threadInfosObj], ); - const resolvedThreadInfosArray = useResolvedThreadInfos(threadInfosArray); + const resolvedThreadInfosArray = useResolvedThreadInfos( + threadInfosArray, + options, + ); return React.useMemo(() => { const obj = {}; for (const resolvedThreadInfo of resolvedThreadInfosArray) { diff --git a/lib/utils/entity-text.js b/lib/utils/entity-text.js index d56a0d1f05..e2f6d58dab 100644 --- a/lib/utils/entity-text.js +++ b/lib/utils/entity-text.js @@ -6,7 +6,7 @@ import t, { type TInterface, type TUnion } from 'tcomb'; import type { GetENSNames } from './ens-helpers.js'; import { tID, tShape, tString } from './validation-utils.js'; -import { useENSNames } from '../hooks/ens-cache.js'; +import { useENSNames, type UseENSNamesOptions } from '../hooks/ens-cache.js'; import { threadNoun } from '../shared/thread-utils.js'; import { stringForUser } from '../shared/user-utils.js'; import { @@ -533,12 +533,15 @@ function entityTextFromObjects( .filter(Boolean); } -function useENSNamesForEntityText(entityText: ?EntityText): ?EntityText { +function useENSNamesForEntityText( + entityText: ?EntityText, + options?: ?UseENSNamesOptions, +): ?EntityText { const allObjects = React.useMemo( () => (entityText ? entityTextToObjects(entityText) : []), [entityText], ); - const objectsWithENSNames = useENSNames(allObjects); + const objectsWithENSNames = useENSNames(allObjects, options); return React.useMemo( () => entityText ? entityTextFromObjects(objectsWithENSNames) : entityText,