Skip to content

Commit

Permalink
refactor: replace underscore debounce with use-debounce (#18586)
Browse files Browse the repository at this point in the history
* refactor: replace underscore debounce with use-debounce

* refactor: simplify debounced callbacks across components

* refactor(FullSearch): replace useDebounce with useDebouncedCallback for improved performance

* refactor: replace useDebounce with useDebouncedCallback in PeopleTab and ServicesTab for improved performance
  • Loading branch information
olafsulich authored Jan 15, 2025
1 parent 54c1432 commit bff2faf
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 245 deletions.
47 changes: 31 additions & 16 deletions src/script/components/FadingScrollbar/FadingScrollbar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,48 +17,58 @@
*
*/

import {fireEvent, render} from '@testing-library/react';
import underscore from 'underscore';
import {fireEvent, render, act} from '@testing-library/react';

import {FadingScrollbar, parseColor} from './FadingScrollbar';

jest.useFakeTimers();

describe('FadingScrollbar', () => {
let step: () => void = () => {};
beforeEach(() => {
jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb: any) => (step = cb));
});

afterEach(() => {
jest.clearAllTimers();
});

const getAlpha = (element: HTMLElement) => {
const [, , , a] = parseColor(element.style.getPropertyValue('--scrollbar-color'));
return a;
};

it('fade scrollbar in when mouse enters and fades out after debounce', () => {
let runDebounce = () => {};
jest.spyOn(underscore, 'debounce').mockImplementation((cb: any): any => {
const callback = (args: any) => {
runDebounce = () => cb(args);
};
return callback;
});

const {getByTestId} = render(
<FadingScrollbar data-uie-name="fading-scrollbar" style={{'--scrollbar-color': 'rgba(0,0,0,0)'} as any}>
<div>hello</div>
</FadingScrollbar>,
);
const scrollingElement = getByTestId('fading-scrollbar');
fireEvent.mouseEnter(scrollingElement);

act(() => {
fireEvent.mouseEnter(scrollingElement);
});
expect(getAlpha(scrollingElement)).toEqual(0.05);

// Run fade in animation
for (let i = 0; i < 20; i++) {
step();
act(() => {
step();
});
}
expect(getAlpha(scrollingElement)).toBeGreaterThanOrEqual(1);

runDebounce();
// Fast forward past the debounce time
act(() => {
jest.advanceTimersByTime(1000);
});

// Run fade out animation
for (let i = 0; i < 20; i++) {
step();
act(() => {
step();
});
}
expect(getAlpha(scrollingElement)).toBeLessThanOrEqual(0);
});
Expand All @@ -71,12 +81,17 @@ describe('FadingScrollbar', () => {
);

const scrollingElement = getByTestId('fading-scrollbar');
fireEvent.mouseLeave(scrollingElement);

act(() => {
fireEvent.mouseLeave(scrollingElement);
});

expect(getAlpha(scrollingElement)).toEqual(0.95);

for (let i = 0; i < 20; i++) {
step();
act(() => {
step();
});
}
expect(getAlpha(scrollingElement)).toBeLessThanOrEqual(0);
});
Expand Down
6 changes: 4 additions & 2 deletions src/script/components/FadingScrollbar/FadingScrollbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import React, {forwardRef, useRef} from 'react';

import {debounce} from 'underscore';
import {useDebouncedCallback} from 'use-debounce';

const config = {
ANIMATION_STEP: 0.05,
Expand Down Expand Up @@ -83,7 +83,9 @@ export const FadingScrollbar = forwardRef<HTMLDivElement, React.HTMLAttributes<H

const fadeIn = (element: HTMLElement) => startAnimation('fadein', element);
const fadeOut = (element: HTMLElement) => startAnimation('fadeout', element);
const debouncedFadeOut = debounce(fadeOut, config.DEBOUNCE_THRESHOLD);

const debouncedFadeOut = useDebouncedCallback((element: HTMLElement) => fadeOut(element), config.DEBOUNCE_THRESHOLD);

const fadeInIdle = (element: HTMLElement) => {
fadeIn(element);
debouncedFadeOut(element);
Expand Down
14 changes: 7 additions & 7 deletions src/script/components/MessagesList/JumpToLastMessageButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import {HTMLProps, useEffect, useState} from 'react';

import {debounce} from 'underscore';
import {useDebouncedCallback} from 'use-debounce';

import {ChevronIcon, IconButton} from '@wireapp/react-ui-kit';

Expand All @@ -39,16 +39,16 @@ export interface JumpToLastMessageButtonProps extends HTMLProps<HTMLElement> {
export const JumpToLastMessageButton = ({onGoToLastMessage, conversation}: JumpToLastMessageButtonProps) => {
const [isLastMessageVisible, setIsLastMessageVisible] = useState(conversation.isLastMessageVisible());

const debouncedSetVisibility = useDebouncedCallback((value: boolean) => {
setIsLastMessageVisible(value);
}, 200);

useEffect(() => {
const subscription = conversation.isLastMessageVisible.subscribe(
debounce(value => {
setIsLastMessageVisible(value);
}, 200),
);
const subscription = conversation.isLastMessageVisible.subscribe(debouncedSetVisibility);
return () => {
subscription.dispose();
};
}, [conversation]);
}, [conversation, debouncedSetVisibility]);

if (isLastMessageVisible) {
return null;
Expand Down
33 changes: 15 additions & 18 deletions src/script/components/UserSearchableList/UserSearchableList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
*
*/

import React, {useCallback, useEffect, useState} from 'react';
import React, {useEffect, useState} from 'react';

import {QualifiedId} from '@wireapp/api-client/lib/user';
import {container} from 'tsyringe';
import {debounce} from 'underscore';
import {useDebouncedCallback} from 'use-debounce';

import {UserList} from 'Components/UserList';
import {partition} from 'Util/ArrayUtil';
Expand Down Expand Up @@ -83,22 +83,19 @@ export const UserSearchableList = ({
* Try to load additional members from the backend.
* This is needed for large teams (>= 2000 members)
*/
const fetchMembersFromBackend = useCallback(
debounce(async (query: string, ignoreMembers: User[]) => {
const resultUsers = await searchRepository.searchByName(query, selfUser.teamId);
const selfTeamId = selfUser.teamId;
const foundMembers = resultUsers.filter(user => user.teamId === selfTeamId);
const ignoreIds = ignoreMembers.map(member => member.id);
const uniqueMembers = foundMembers.filter(member => !ignoreIds.includes(member.id));

// We shouldn't show any members that have the 'external' role and are not already locally known.
const nonExternalMembers = await teamRepository.filterExternals(uniqueMembers);
setRemoteTeamMembers(
filterRemoteTeamUsers ? await teamRepository.filterRemoteDomainUsers(nonExternalMembers) : nonExternalMembers,
);
}, 300),
[],
);
const fetchMembersFromBackend = useDebouncedCallback(async (query: string, ignoreMembers: User[]) => {
const resultUsers = await searchRepository.searchByName(query, selfUser.teamId);
const selfTeamId = selfUser.teamId;
const foundMembers = resultUsers.filter(user => user.teamId === selfTeamId);
const ignoreIds = ignoreMembers.map(member => member.id);
const uniqueMembers = foundMembers.filter(member => !ignoreIds.includes(member.id));

// We shouldn't show any members that have the 'external' role and are not already locally known.
const nonExternalMembers = await teamRepository.filterExternals(uniqueMembers);
setRemoteTeamMembers(
filterRemoteTeamUsers ? await teamRepository.filterRemoteDomainUsers(nonExternalMembers) : nonExternalMembers,
);
}, 300);

// Filter all list items if a filter is provided

Expand Down
90 changes: 0 additions & 90 deletions src/script/hooks/useDebounce.test.tsx

This file was deleted.

29 changes: 0 additions & 29 deletions src/script/hooks/useDebounce.ts

This file was deleted.

Loading

0 comments on commit bff2faf

Please sign in to comment.