Skip to content

Commit 7392998

Browse files
committed
refactor: replace @reactuses/core with ahooks
- Update apps/web/package.json dependency - Update react-best-practices skill documentation to use ahooks hooks - useEventListener, useLatest, useDebounceFn, useResponsive, useThrottle
1 parent a517317 commit 7392998

3 files changed

Lines changed: 90 additions & 63 deletions

File tree

.agent/skills/react-best-practices/AGENTS.md

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -729,7 +729,7 @@ function Dashboard() {
729729

730730
**Impact: MEDIUM (prevents memory leaks and redundant handlers)**
731731

732-
Use `@reactuses/core` hooks for global events to prevent multiple subscriptions and ensure proper cleanup.
732+
Use `ahooks` hooks for global events to prevent multiple subscriptions and ensure proper cleanup.
733733

734734
**Incorrect: each component creates its own listener**
735735

@@ -751,19 +751,22 @@ function Component2() {
751751
}
752752
```
753753

754-
**Correct: use @reactuses/core hooks**
754+
**Correct: use ahooks hooks**
755755

756756
```tsx
757-
import { useEventListener, useWindowSize } from '@reactuses/core'
757+
import { useEventListener } from 'ahooks'
758758

759759
// Option 1: Listen to resize event directly
760760
function Component1() {
761761
useEventListener('resize', () => console.log('resize'))
762762
}
763763

764-
// Option 2: Get window dimensions reactively
764+
// Option 2: Get window dimensions reactively with useEventListener
765765
function Component2() {
766-
const { width, height } = useWindowSize()
766+
const [size, setSize] = useState({ width: 0, height: 0 })
767+
useEventListener('resize', () => {
768+
setSize({ width: window.innerWidth, height: window.innerHeight })
769+
})
767770
// Automatically updates on resize
768771
}
769772
```
@@ -889,7 +892,7 @@ function Profile({ user }: { user: User }) {
889892

890893
**Impact: MEDIUM (reduces re-render frequency)**
891894

892-
Subscribe to derived boolean state instead of continuous values to reduce re-render frequency. Use `useMediaQuery` from `@reactuses/core`.
895+
Subscribe to derived boolean state instead of continuous values to reduce re-render frequency. Use `useResponsive` from `ahooks`.
893896

894897
**Incorrect: re-renders on every pixel change**
895898

@@ -901,18 +904,19 @@ function Sidebar() {
901904
}
902905
```
903906

904-
**Correct: re-renders only when boolean changes**
907+
**Correct: re-renders only when breakpoint changes**
905908

906909
```tsx
907-
import { useMediaQuery } from '@reactuses/core'
910+
import { useResponsive } from 'ahooks'
908911

909912
function Sidebar() {
910-
const isMobile = useMediaQuery('(max-width: 767px)')
913+
const responsive = useResponsive()
914+
const isMobile = !responsive.md // md breakpoint is 768px by default
911915
return <nav className={isMobile ? 'mobile' : 'desktop'}>
912916
}
913917
```
914918

915-
`useMediaQuery` uses `useSyncExternalStore` internally for optimized re-rendering and only triggers updates when the query match state changes.
919+
`useResponsive` uses breakpoint-based updates and only triggers re-renders when the breakpoint state changes, not on every pixel.
916920

917921
### 5.5 Use Functional setState Updates
918922

@@ -1050,7 +1054,7 @@ For simple primitives (`useState(0)`), direct references (`useState(props.value)
10501054

10511055
**Impact: MEDIUM (maintains UI responsiveness)**
10521056

1053-
Mark frequent, non-urgent state updates as transitions to maintain UI responsiveness. Alternatively, use throttling from `@reactuses/core`.
1057+
Mark frequent, non-urgent state updates as transitions to maintain UI responsiveness. Alternatively, use throttling from `ahooks`.
10541058

10551059
**Incorrect: blocks UI on every scroll**
10561060

@@ -1082,18 +1086,16 @@ function ScrollTracker() {
10821086
}
10831087
```
10841088

1085-
**Alternative: throttling with @reactuses/core**
1089+
**Alternative: throttling with ahooks**
10861090

10871091
```tsx
1088-
import { useEventListener, useThrottle } from '@reactuses/core'
1092+
import { useEventListener, useThrottle } from 'ahooks'
10891093

10901094
function ScrollTracker() {
10911095
const [scrollY, setScrollY] = useState(0)
1092-
const throttledScrollY = useThrottle(scrollY, 100) // Update max 10x/sec
1096+
const throttledScrollY = useThrottle(scrollY, { wait: 100 }) // Update max 10x/sec
10931097

1094-
useEventListener('scroll', () => setScrollY(window.scrollY), {
1095-
passive: true
1096-
})
1098+
useEventListener('scroll', () => setScrollY(window.scrollY))
10971099

10981100
// Use throttledScrollY for rendering
10991101
return <div>Scroll: {throttledScrollY}px</div>
@@ -1929,7 +1931,7 @@ Advanced patterns for specific cases that require careful implementation.
19291931

19301932
**Impact: LOW (stable subscriptions)**
19311933

1932-
Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes. Prefer `@reactuses/core` hooks which handle this internally.
1934+
Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes. Prefer `ahooks` hooks which handle this internally.
19331935

19341936
**Incorrect: re-subscribes on every render**
19351937

@@ -1942,10 +1944,10 @@ function useWindowEvent(event: string, handler: () => void) {
19421944
}
19431945
```
19441946

1945-
**Correct: use @reactuses/core useEventListener**
1947+
**Correct: use ahooks useEventListener**
19461948

19471949
```tsx
1948-
import { useEventListener } from '@reactuses/core'
1950+
import { useEventListener } from 'ahooks'
19491951

19501952
function Component() {
19511953
// Handler is stored in a ref internally - no re-subscription on handler change
@@ -1976,7 +1978,7 @@ function useWindowEvent(event: string, handler: () => void) {
19761978

19771979
**Impact: LOW (prevents effect re-runs)**
19781980

1979-
Access latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures. Use `useLatest` from `@reactuses/core`.
1981+
Access latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures. Use `useLatest` from `ahooks`.
19801982

19811983
**Incorrect: effect re-runs on every callback change**
19821984

@@ -1991,10 +1993,10 @@ function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
19911993
}
19921994
```
19931995

1994-
**Correct: stable effect, fresh callback with @reactuses/core**
1996+
**Correct: stable effect, fresh callback with ahooks**
19951997

19961998
```tsx
1997-
import { useLatest } from '@reactuses/core'
1999+
import { useLatest } from 'ahooks'
19982000

19992001
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
20002002
const [query, setQuery] = useState('')
@@ -2007,24 +2009,24 @@ function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
20072009
}
20082010
```
20092011

2010-
**Alternative: use useDebounce for this specific pattern**
2012+
**Alternative: use useDebounceFn for this specific pattern**
20112013

20122014
```tsx
2013-
import { useDebounce } from '@reactuses/core'
2015+
import { useDebounceFn } from 'ahooks'
20142016

20152017
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
20162018
const [query, setQuery] = useState('')
2017-
const debouncedQuery = useDebounce(query, 300)
2019+
const { run: debouncedSearch } = useDebounceFn(onSearch, { wait: 300 })
20182020

20192021
useEffect(() => {
2020-
if (debouncedQuery) {
2021-
onSearch(debouncedQuery)
2022+
if (query) {
2023+
debouncedSearch(query)
20222024
}
2023-
}, [debouncedQuery, onSearch])
2025+
}, [query, debouncedSearch])
20242026
}
20252027
```
20262028

2027-
For debounced search specifically, `useDebounce` provides a cleaner solution.
2029+
For debounced search specifically, `useDebounceFn` provides a cleaner solution.
20282030

20292031
---
20302032

@@ -2034,5 +2036,5 @@ For debounced search specifically, `useDebounce` provides a cleaner solution.
20342036
2. [https://nextjs.org](https://nextjs.org)
20352037
3. [https://tanstack.com/query](https://tanstack.com/query)
20362038
4. [https://github.com/shuding/better-all](https://github.com/shuding/better-all)
2037-
5. [https://reactuse.com](https://reactuse.com) - @reactuses/core hooks library
2039+
5. [https://ahooks.js.org](https://ahooks.js.org) - ahooks React hooks library
20382040

apps/web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"@opentelemetry/sdk-node": "^0.208.0",
2525
"@opentelemetry/sdk-trace-base": "^2.2.0",
2626
"@opentelemetry/semantic-conventions": "^1.38.0",
27-
"@reactuses/core": "^6.1.8",
27+
"ahooks": "^3.8.5",
2828
"@serwist/turbopack": "^9.5.0",
2929
"@t3-oss/env-nextjs": "^0.13.10",
3030
"@tanstack/react-devtools": "^0.9.1",

0 commit comments

Comments
 (0)