Skip to content

Commit e1feaab

Browse files
committed
perf(social): debounce search query and memoize filtered users list
1 parent c654ef1 commit e1feaab

1 file changed

Lines changed: 25 additions & 13 deletions

File tree

src/components/social/FollowingSystem.tsx

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22
import Image from 'next/image';
3-
import { useState, useEffect } from 'react';
3+
import { useState, useEffect, useMemo } from 'react';
44
import { Search, UserCircle } from 'lucide-react';
55
import { useFollowUser } from '@/hooks/useSocialFeatures';
66
import { apiClient } from '@/lib/api';
@@ -40,11 +40,10 @@ function UserRow({ user }: { user: SocialUser }) {
4040
<button
4141
onClick={isFollowing ? unfollow : follow}
4242
disabled={loading}
43-
className={`px-3 py-1 rounded-lg text-xs font-medium transition-colors disabled:opacity-50 ${
44-
isFollowing
45-
? 'border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800'
46-
: 'bg-blue-600 text-white hover:bg-blue-500'
47-
}`}
43+
className={`px-3 py-1 rounded-lg text-xs font-medium transition-colors disabled:opacity-50 ${isFollowing
44+
? 'border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800'
45+
: 'bg-blue-600 text-white hover:bg-blue-500'
46+
}`}
4847
>
4948
{isFollowing ? 'Unfollow' : 'Follow'}
5049
</button>
@@ -56,8 +55,18 @@ export default function FollowingSystem({ userId }: FollowingSystemProps) {
5655
const [tab, setTab] = useState<ListTab>('followers');
5756
const [users, setUsers] = useState<SocialUser[]>([]);
5857
const [query, setQuery] = useState('');
58+
const [debouncedQuery, setDebouncedQuery] = useState('');
5959
const [loading, setLoading] = useState(false);
6060

61+
// Debounce the search input query by 300ms
62+
useEffect(() => {
63+
const timer = setTimeout(() => {
64+
setDebouncedQuery(query);
65+
}, 300);
66+
67+
return () => clearTimeout(timer);
68+
}, [query]);
69+
6170
useEffect(() => {
6271
setLoading(true);
6372
apiClient
@@ -67,7 +76,11 @@ export default function FollowingSystem({ userId }: FollowingSystemProps) {
6776
.finally(() => setLoading(false));
6877
}, [tab, userId]);
6978

70-
const filtered = users.filter((u) => u.name.toLowerCase().includes(query.toLowerCase()));
79+
// Memoize the filtered user results based on the debounced query
80+
const filtered = useMemo(() => {
81+
const lowercaseQuery = debouncedQuery.toLowerCase();
82+
return users.filter((u) => u.name.toLowerCase().includes(lowercaseQuery));
83+
}, [users, debouncedQuery]);
7184

7285
return (
7386
<div className="bg-white dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-700">
@@ -77,11 +90,10 @@ export default function FollowingSystem({ userId }: FollowingSystemProps) {
7790
<button
7891
key={t}
7992
onClick={() => setTab(t)}
80-
className={`flex-1 py-3 text-sm font-medium capitalize transition-colors ${
81-
tab === t
82-
? 'border-b-2 border-blue-600 text-blue-600 dark:text-blue-400'
83-
: 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200'
84-
}`}
93+
className={`flex-1 py-3 text-sm font-medium capitalize transition-colors ${tab === t
94+
? 'border-b-2 border-blue-600 text-blue-600 dark:text-blue-400'
95+
: 'text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200'
96+
}`}
8597
>
8698
{t}
8799
</button>
@@ -118,4 +130,4 @@ export default function FollowingSystem({ userId }: FollowingSystemProps) {
118130
</div>
119131
</div>
120132
);
121-
}
133+
}

0 commit comments

Comments
 (0)