Skip to content

Commit 3761c65

Browse files
committed
feat(hooks): destroy transient scope when unmount
1 parent 7e750d7 commit 3761c65

File tree

2 files changed

+36
-4
lines changed

2 files changed

+36
-4
lines changed

src/hooks.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as React from 'react'
2+
import { get } from 'lodash'
23

34
import {
45
Ayanami,
@@ -7,11 +8,19 @@ import {
78
ScopeConfig,
89
ActionMethodOfAyanami,
910
ConstructorOf,
11+
TransientScope,
1012
} from './core'
1113

1214
export type HooksResult<M extends Ayanami<S>, S> = [Readonly<S>, ActionMethodOfAyanami<M, S>]
1315

14-
export function useAyanamiInstance<M extends Ayanami<S>, S>(ayanami: M): HooksResult<M, S> {
16+
interface UseAyanamiInstanceConfig {
17+
destroyWhenUnmount?: boolean
18+
}
19+
20+
export function useAyanamiInstance<M extends Ayanami<S>, S>(
21+
ayanami: M,
22+
config?: UseAyanamiInstanceConfig,
23+
): HooksResult<M, S> {
1524
const ikari = React.useMemo(() => combineWithIkari(ayanami), [ayanami])
1625
const [state, setState] = React.useState<S>(() => ayanami.getState())
1726

@@ -20,6 +29,17 @@ export function useAyanamiInstance<M extends Ayanami<S>, S>(ayanami: M): HooksRe
2029
return () => subscription.unsubscribe()
2130
}, [])
2231

32+
React.useEffect(
33+
() => () => {
34+
const isDestroyWhenUnmount = get(config, 'destroyWhenUnmount', false)
35+
36+
if (isDestroyWhenUnmount) {
37+
ayanami.destroy()
38+
}
39+
},
40+
[],
41+
)
42+
2343
return [state, ikari.triggerActions] as HooksResult<M, S>
2444
}
2545

@@ -29,5 +49,10 @@ export function useAyanami<M extends Ayanami<S>, S>(
2949
): M extends Ayanami<infer SS> ? HooksResult<M, SS> : HooksResult<M, S> {
3050
const ayanami = React.useMemo(() => getInstanceWithScope(A, config), [A])
3151

32-
return useAyanamiInstance<M, S>(ayanami) as any
52+
const useAyanamiInstanceConfig = React.useMemo((): UseAyanamiInstanceConfig => {
53+
const scope = get(config, 'scope', false)
54+
return { destroyWhenUnmount: scope === TransientScope }
55+
}, [])
56+
57+
return useAyanamiInstance<M, S>(ayanami, useAyanamiInstanceConfig) as any
3358
}

test/specs/hooks.spec.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Injectable } from '@asuka/di'
22
import * as React from 'react'
3-
import { act, create, ReactTestInstance } from 'react-test-renderer'
3+
import { act, create, ReactTestInstance, ReactTestRenderer } from 'react-test-renderer'
44
import { Observable } from 'rxjs'
55
import { map, withLatestFrom } from 'rxjs/operators'
66

@@ -152,9 +152,10 @@ describe('Hooks spec:', () => {
152152
describe('TransientScope will isolate state and actions', () => {
153153
let count: () => string | ReactTestInstance
154154
let click: (action: CountAction) => void
155+
let testRenderer: ReactTestRenderer
155156

156157
beforeEach(() => {
157-
const testRenderer = create(<CountComponent scope={TransientScope} />)
158+
testRenderer = create(<CountComponent scope={TransientScope} />)
158159

159160
count = () => testRenderer.root.findByType('span').children[0]
160161
click = (action: CountAction) =>
@@ -177,6 +178,12 @@ describe('Hooks spec:', () => {
177178
click(CountAction.MINUS)
178179
expect(count()).toBe('-1')
179180
})
181+
182+
it('should destroy when component unmount', () => {
183+
const spy = jest.spyOn(Ayanami.prototype, 'destroy')
184+
act(() => testRenderer.unmount())
185+
expect(spy.mock.calls.length).toBe(1)
186+
})
180187
})
181188
})
182189
})

0 commit comments

Comments
 (0)