Skip to content

Commit c424564

Browse files
authored
Merge pull request #465 from InfiniteXyy/master
perf: avoid using selector each render
2 parents f7e0d8c + bfb2f83 commit c424564

File tree

3 files changed

+22
-17
lines changed

3 files changed

+22
-17
lines changed

src/hooks/shallow-equal.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
export const shallowEqual = (a: any, b: any): boolean => {
22
if (a === b) return true
33
if (typeof a === 'object' && typeof b === 'object' && a !== null && b !== null) {
4-
return (
5-
Object.keys(a).length === Object.keys(b).length &&
6-
Object.keys(a).every((key) => a[key] === b[key])
7-
)
4+
const keys = Object.keys(a)
5+
if (keys.length !== Object.keys(b).length) return false
6+
return keys.every((key) => key in b && a[key] === b[key])
87
}
98
return false
109
}

src/hooks/use-ayanami.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ interface Config<S, U> extends Partial<ScopeConfig> {
2323
selector?: (state: S) => U
2424
}
2525

26-
export function useAyanami<M extends Ayanami<any>, U = M extends Ayanami<infer S> ? S : never>(
26+
export function useAyanami<M extends Ayanami<S>, S, U = M extends Ayanami<infer SS> ? SS : never>(
2727
A: ConstructorOf<M>,
2828
config?: M extends Ayanami<infer S> ? Config<S, U> : never,
2929
): M extends Ayanami<infer S>
@@ -38,7 +38,7 @@ export function useAyanami<M extends Ayanami<any>, U = M extends Ayanami<infer S
3838
const ayanami = React.useMemo(() => getInstanceWithScope(A, reqScope), [reqScope])
3939
ayanami.scopeName = scope || DEFAULT_SCOPE_NAME
4040

41-
const useAyanamiInstanceConfig = React.useMemo<UseAyanamiInstanceConfig>(() => {
41+
const useAyanamiInstanceConfig = React.useMemo<UseAyanamiInstanceConfig<S>>(() => {
4242
return { destroyWhenUnmount: scope === TransientScope, selector }
4343
}, [reqScope])
4444

src/hooks/use-subscribe-ayanami-state.ts

+17-11
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,16 @@ import identity from 'lodash/identity'
44
import { shallowEqual } from './shallow-equal'
55
import { Ayanami } from '../core'
66

7-
export function useSubscribeAyanamiState<M extends Ayanami<S>, S, U>(
7+
export function useSubscribeAyanamiState<M extends Ayanami<S>, S, U = S>(
88
ayanami: M,
99
selector: (state: S) => U = identity,
1010
): unknown {
11-
const state = ayanami.getState()
11+
const [state, setState] = React.useState<U>(() => selector(ayanami.getState()))
1212

1313
const ayanamiRef = React.useRef<Ayanami<S> | null>(null)
1414
const subscriptionRef = React.useRef<Subscription | null>(null)
15-
const stateRef = React.useRef<S>(state)
16-
17-
const [, forceUpdate] = React.useState({})
15+
const stateRef = React.useRef<U>(state)
16+
const isFirstRenderRef = React.useRef(true)
1817

1918
if (ayanamiRef.current !== ayanami) {
2019
ayanamiRef.current = ayanami
@@ -25,11 +24,17 @@ export function useSubscribeAyanamiState<M extends Ayanami<S>, S, U>(
2524
}
2625

2726
if (ayanami) {
28-
subscriptionRef.current = ayanami.getState$().subscribe((state) => {
29-
const before = selector(stateRef.current)
30-
const after = selector(state)
31-
if (!shallowEqual(before, after)) forceUpdate({})
32-
stateRef.current = state
27+
subscriptionRef.current = ayanami.getState$().subscribe((moduleState) => {
28+
if (isFirstRenderRef.current) return
29+
if (selector === identity) {
30+
setState(selector(moduleState))
31+
stateRef.current = selector(moduleState)
32+
} else {
33+
const before = stateRef.current
34+
const after = selector(moduleState)
35+
if (!shallowEqual(before, after)) setState(after)
36+
stateRef.current = after
37+
}
3338
})
3439
}
3540
}
@@ -43,5 +48,6 @@ export function useSubscribeAyanamiState<M extends Ayanami<S>, S, U>(
4348
[subscriptionRef],
4449
)
4550

46-
return selector(state)
51+
isFirstRenderRef.current = false
52+
return state
4753
}

0 commit comments

Comments
 (0)