Skip to content

Commit 630a05e

Browse files
authored
chore(deps): update eslint-plugin-react-hooks and fix linting issues (#233)
* build(dep): bump eslint-plugin-react-hooks * refactor: move SimpleBarList component outside of Analytics component * refactor: defer state update in DataTableBulkActions to avoid cascading renders * refactor: update mock user authentication to compute expiry at success time * refactor: optimize LongText and NewChat components by removing unnecessary useEffect hooks
1 parent 934561d commit 630a05e

File tree

7 files changed

+103
-73
lines changed

7 files changed

+103
-73
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
"@types/react-dom": "^19.2.1",
7171
"@vitejs/plugin-react-swc": "^4.1.0",
7272
"eslint": "^9.37.0",
73-
"eslint-plugin-react-hooks": "^5.2.0",
73+
"eslint-plugin-react-hooks": "^7.0.0",
7474
"eslint-plugin-react-refresh": "^0.4.23",
7575
"globals": "^16.4.0",
7676
"knip": "^5.64.2",

pnpm-lock.yaml

Lines changed: 35 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/data-table/bulk-actions.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ export function DataTableBulkActions<TData>({
4141
useEffect(() => {
4242
if (selectedCount > 0) {
4343
const message = `${selectedCount} ${entityName}${selectedCount > 1 ? 's' : ''} selected. Bulk actions toolbar is available.`
44-
setAnnouncement(message)
44+
45+
// Use queueMicrotask to defer state update and avoid cascading renders
46+
queueMicrotask(() => {
47+
setAnnouncement(message)
48+
})
4549

4650
// Clear announcement after a delay
4751
const timer = setTimeout(() => setAnnouncement(''), 3000)

src/components/long-text.tsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useRef, useState } from 'react'
1+
import { useRef, useState } from 'react'
22
import { cn } from '@/lib/utils'
33
import {
44
Popover,
@@ -26,18 +26,17 @@ export function LongText({
2626
const ref = useRef<HTMLDivElement>(null)
2727
const [isOverflown, setIsOverflown] = useState(false)
2828

29-
useEffect(() => {
30-
if (checkOverflow(ref.current)) {
31-
setIsOverflown(true)
32-
return
29+
// Use ref callback to check overflow when element is mounted
30+
const refCallback = (node: HTMLDivElement | null) => {
31+
ref.current = node
32+
if (node && checkOverflow(node)) {
33+
queueMicrotask(() => setIsOverflown(true))
3334
}
34-
35-
setIsOverflown(false)
36-
}, [])
35+
}
3736

3837
if (!isOverflown)
3938
return (
40-
<div ref={ref} className={cn('truncate', className)}>
39+
<div ref={refCallback} className={cn('truncate', className)}>
4140
{children}
4241
</div>
4342
)
@@ -48,7 +47,7 @@ export function LongText({
4847
<TooltipProvider delayDuration={0}>
4948
<Tooltip>
5049
<TooltipTrigger asChild>
51-
<div ref={ref} className={cn('truncate', className)}>
50+
<div ref={refCallback} className={cn('truncate', className)}>
5251
{children}
5352
</div>
5453
</TooltipTrigger>
@@ -61,7 +60,7 @@ export function LongText({
6160
<div className='sm:hidden'>
6261
<Popover>
6362
<PopoverTrigger asChild>
64-
<div ref={ref} className={cn('truncate', className)}>
63+
<div ref={refCallback} className={cn('truncate', className)}>
6564
{children}
6665
</div>
6766
</PopoverTrigger>

src/features/auth/sign-in/components/user-auth-form.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,19 @@ export function UserAuthForm({
5454
function onSubmit(data: z.infer<typeof formSchema>) {
5555
setIsLoading(true)
5656

57-
// Mock successful authentication
58-
const mockUser = {
59-
accountNo: 'ACC001',
60-
email: data.email,
61-
role: ['user'],
62-
exp: Date.now() + 24 * 60 * 60 * 1000, // 24 hours from now
63-
}
64-
6557
toast.promise(sleep(2000), {
6658
loading: 'Signing in...',
6759
success: () => {
6860
setIsLoading(false)
6961

62+
// Mock successful authentication with expiry computed at success time
63+
const mockUser = {
64+
accountNo: 'ACC001',
65+
email: data.email,
66+
role: ['user'],
67+
exp: Date.now() + 24 * 60 * 60 * 1000, // 24 hours from now
68+
}
69+
7070
// Set user and access token
7171
auth.setUser(mockUser)
7272
auth.setAccessToken('mock-access-token')

src/features/chats/components/new-chat.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState } from 'react'
1+
import { useState } from 'react'
22
import { Check, X } from 'lucide-react'
33
import { showSubmittedData } from '@/lib/show-submitted-data'
44
import { Badge } from '@/components/ui/badge'
@@ -41,14 +41,16 @@ export function NewChat({ users, onOpenChange, open }: NewChatProps) {
4141
setSelectedUsers(selectedUsers.filter((user) => user.id !== userId))
4242
}
4343

44-
useEffect(() => {
45-
if (!open) {
44+
const handleOpenChange = (newOpen: boolean) => {
45+
onOpenChange(newOpen)
46+
// Reset selected users when dialog closes
47+
if (!newOpen) {
4648
setSelectedUsers([])
4749
}
48-
}, [open])
50+
}
4951

5052
return (
51-
<Dialog open={open} onOpenChange={onOpenChange}>
53+
<Dialog open={open} onOpenChange={handleOpenChange}>
5254
<DialogContent className='sm:max-w-[600px]'>
5355
<DialogHeader>
5456
<DialogTitle>New message</DialogTitle>

src/features/dashboard/components/analytics.tsx

Lines changed: 37 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,47 +8,6 @@ import {
88
import { AnalyticsChart } from './analytics-chart'
99

1010
export function Analytics() {
11-
type Item = { name: string; value: number }
12-
13-
const SimpleBarList = ({
14-
items,
15-
valueFormatter,
16-
barClass,
17-
}: {
18-
items: Item[]
19-
valueFormatter: (n: number) => string
20-
barClass: string
21-
}) => {
22-
const max = Math.max(...items.map((i) => i.value), 1)
23-
return (
24-
<ul className='space-y-3'>
25-
{items.map((i) => {
26-
const width = `${Math.round((i.value / max) * 100)}%`
27-
return (
28-
<li
29-
key={i.name}
30-
className='flex items-center justify-between gap-3'
31-
>
32-
<div className='min-w-0 flex-1'>
33-
<div className='text-muted-foreground mb-1 truncate text-xs'>
34-
{i.name}
35-
</div>
36-
<div className='bg-muted h-2.5 w-full rounded-full'>
37-
<div
38-
className={`h-2.5 rounded-full ${barClass}`}
39-
style={{ width }}
40-
/>
41-
</div>
42-
</div>
43-
<div className='ps-2 text-xs font-medium tabular-nums'>
44-
{valueFormatter(i.value)}
45-
</div>
46-
</li>
47-
)
48-
})}
49-
</ul>
50-
)
51-
}
5211
return (
5312
<div className='space-y-4'>
5413
<Card>
@@ -191,3 +150,40 @@ export function Analytics() {
191150
</div>
192151
)
193152
}
153+
154+
function SimpleBarList({
155+
items,
156+
valueFormatter,
157+
barClass,
158+
}: {
159+
items: { name: string; value: number }[]
160+
valueFormatter: (n: number) => string
161+
barClass: string
162+
}) {
163+
const max = Math.max(...items.map((i) => i.value), 1)
164+
return (
165+
<ul className='space-y-3'>
166+
{items.map((i) => {
167+
const width = `${Math.round((i.value / max) * 100)}%`
168+
return (
169+
<li key={i.name} className='flex items-center justify-between gap-3'>
170+
<div className='min-w-0 flex-1'>
171+
<div className='text-muted-foreground mb-1 truncate text-xs'>
172+
{i.name}
173+
</div>
174+
<div className='bg-muted h-2.5 w-full rounded-full'>
175+
<div
176+
className={`h-2.5 rounded-full ${barClass}`}
177+
style={{ width }}
178+
/>
179+
</div>
180+
</div>
181+
<div className='ps-2 text-xs font-medium tabular-nums'>
182+
{valueFormatter(i.value)}
183+
</div>
184+
</li>
185+
)
186+
})}
187+
</ul>
188+
)
189+
}

0 commit comments

Comments
 (0)