[Perf] Chat List: Some optimizations

This commit is contained in:
Alexander Zinchuk 2021-07-15 01:32:24 +03:00
parent 5f00ef8ab6
commit 7b00e471ad
4 changed files with 51 additions and 16 deletions

View File

@ -90,11 +90,15 @@ const ChatList: FC<OwnProps & StateProps & DispatchProps> = ({
const prevOrderById = usePrevious(orderById); const prevOrderById = usePrevious(orderById);
const orderDiffById = orderById && prevOrderById const orderDiffById = useMemo(() => {
? mapValues(orderById, (order, id) => { if (!orderById || !prevOrderById) {
return {};
}
return mapValues(orderById, (order, id) => {
return order - (prevOrderById[id] !== undefined ? prevOrderById[id] : Infinity); return order - (prevOrderById[id] !== undefined ? prevOrderById[id] : Infinity);
}) });
: {}; }, [orderById, prevOrderById]);
const loadMoreOfType = useCallback(() => { const loadMoreOfType = useCallback(() => {
loadMoreChats({ listType: folderType === 'archived' ? 'archived' : 'active' }); loadMoreChats({ listType: folderType === 'archived' ? 'archived' : 'active' });

View File

@ -85,8 +85,14 @@ interface ComponentInstance {
onUpdate?: () => void; onUpdate?: () => void;
} }
export type VirtualElement = VirtualElementEmpty | VirtualElementText | VirtualElementTag | VirtualElementComponent; export type VirtualElement =
export type VirtualRealElement = VirtualElementTag | VirtualElementComponent; VirtualElementEmpty
| VirtualElementText
| VirtualElementTag
| VirtualElementComponent;
export type VirtualRealElement =
VirtualElementTag
| VirtualElementComponent;
export type VirtualElementChildren = VirtualElement[]; export type VirtualElementChildren = VirtualElement[];
const Fragment = Symbol('Fragment'); const Fragment = Symbol('Fragment');
@ -545,7 +551,7 @@ export function useLayoutEffect(effect: () => Function | void, dependencies?: an
return useLayoutEffectBase(onTickEnd, effect, dependencies); return useLayoutEffectBase(onTickEnd, effect, dependencies);
} }
export function useMemo<T extends any>(resolver: () => T, dependencies: any[]): T { export function useMemo<T extends any>(resolver: () => T, dependencies: any[], debugKey?: string): T {
const { cursor, byCursor } = renderingInstance.hooks.memos; const { cursor, byCursor } = renderingInstance.hooks.memos;
let { current } = byCursor[cursor] || {}; let { current } = byCursor[cursor] || {};
@ -553,6 +559,16 @@ export function useMemo<T extends any>(resolver: () => T, dependencies: any[]):
byCursor[cursor] === undefined byCursor[cursor] === undefined
|| dependencies.some((dependency, i) => dependency !== byCursor[cursor].dependencies[i]) || dependencies.some((dependency, i) => dependency !== byCursor[cursor].dependencies[i])
) { ) {
if (DEBUG && debugKey) {
// eslint-disable-next-line no-console
console.log(
`[Teact.useMemo] ${renderingInstance.name} (${debugKey}): Update is caused by:`,
byCursor[cursor]
? getUnequalProps(dependencies, byCursor[cursor].dependencies).join(', ')
: '[first render]',
);
}
current = resolver(); current = resolver();
} }

View File

@ -100,20 +100,28 @@ export function updateChats(global: GlobalState, updatedById: Record<number, Api
// @optimization Allows to avoid redundant updates which cause a lot of renders // @optimization Allows to avoid redundant updates which cause a lot of renders
export function addChats(global: GlobalState, addedById: Record<number, ApiChat>): GlobalState { export function addChats(global: GlobalState, addedById: Record<number, ApiChat>): GlobalState {
const { byId } = global.chats; const { byId } = global.chats;
let isAdded = false;
const addedChats = Object.keys(addedById).map(Number).reduce<Record<number, ApiChat>>((acc, id) => { const addedChats = Object.keys(addedById).map(Number).reduce<Record<number, ApiChat>>((acc, id) => {
if (!byId[id] || (byId[id].isMin && !addedById[id].isMin)) { if (!byId[id] || (byId[id].isMin && !addedById[id].isMin)) {
const updatedChat = getUpdatedChat(global, id, addedById[id]); const updatedChat = getUpdatedChat(global, id, addedById[id]);
if (updatedChat) { if (updatedChat) {
acc[id] = updatedChat; acc[id] = updatedChat;
if (!isAdded) {
isAdded = true;
}
} }
} }
return acc; return acc;
}, {}); }, {});
global = replaceChats(global, { if (isAdded) {
...global.chats.byId, global = replaceChats(global, {
...addedChats, ...global.chats.byId,
}); ...addedChats,
});
}
return global; return global;
} }

View File

@ -91,23 +91,30 @@ export function updateUsers(global: GlobalState, updatedById: Record<number, Api
// @optimization Allows to avoid redundant updates which cause a lot of renders // @optimization Allows to avoid redundant updates which cause a lot of renders
export function addUsers(global: GlobalState, addedById: Record<number, ApiUser>): GlobalState { export function addUsers(global: GlobalState, addedById: Record<number, ApiUser>): GlobalState {
const { byId } = global.users; const { byId } = global.users;
let isAdded = false;
const addedUsers = Object.keys(addedById).map(Number).reduce<Record<number, ApiUser>>((acc, id) => { const addedUsers = Object.keys(addedById).map(Number).reduce<Record<number, ApiUser>>((acc, id) => {
if (!byId[id] || (byId[id].isMin && !addedById[id].isMin)) { if (!byId[id] || (byId[id].isMin && !addedById[id].isMin)) {
const updatedUser = getUpdatedUser(global, id, addedById[id]); const updatedUser = getUpdatedUser(global, id, addedById[id]);
if (updatedUser) { if (updatedUser) {
acc[id] = updatedUser; acc[id] = updatedUser;
if (!isAdded) {
isAdded = true;
}
} }
} }
return acc; return acc;
}, {}); }, {});
global = updateContactList(global, Object.values(addedUsers)); if (isAdded) {
global = replaceUsers(global, {
...global.users.byId,
...addedUsers,
});
global = replaceUsers(global, { global = updateContactList(global, Object.values(addedUsers));
...global.users.byId, }
...addedUsers,
});
return global; return global;
} }