Skip to content

Commit 8265fbf

Browse files
committed
Improve hook behaviors when it is disposed
1 parent 0828451 commit 8265fbf

File tree

7 files changed

+70
-8
lines changed

7 files changed

+70
-8
lines changed

Sources/Hooks/Hook/UseReducer.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,19 @@ private struct ReducerHook<State, Action>: Hook {
3737
Ref(initialState: initialState)
3838
}
3939

40-
func makeValue(coordinator: Coordinator) -> (state: State, dispatch: (Action) -> Void) {
40+
func makeValue(coordinator: Coordinator) -> (
41+
state: State,
42+
dispatch: (Action) -> Void
43+
) {
4144
(
4245
state: coordinator.state.state,
4346
dispatch: { action in
4447
assertMainThread()
48+
49+
guard !coordinator.state.isDisposed else {
50+
return
51+
}
52+
4553
coordinator.state.nextAction = action
4654
coordinator.updateView()
4755
}
@@ -56,12 +64,18 @@ private struct ReducerHook<State, Action>: Hook {
5664
coordinator.state.state = reducer(coordinator.state.state, action)
5765
coordinator.state.nextAction = nil
5866
}
67+
68+
func dispose(state: Ref) {
69+
state.isDisposed = true
70+
state.nextAction = nil
71+
}
5972
}
6073

6174
private extension ReducerHook {
6275
final class Ref {
6376
var state: State
6477
var nextAction: Action?
78+
var isDisposed = false
6579

6680
init(initialState: State) {
6781
state = initialState

Sources/Hooks/Hook/UseState.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,27 @@ private struct StateHook<State>: Hook {
2626
state
2727
},
2828
set: { newState in
29+
assertMainThread()
30+
31+
guard !coordinator.state.isDisposed else {
32+
return
33+
}
34+
2935
coordinator.state.state = newState
3036
coordinator.updateView()
3137
}
3238
)
3339
}
40+
41+
func dispose(state: Ref) {
42+
state.isDisposed = true
43+
}
3444
}
3545

3646
private extension StateHook {
3747
final class Ref {
3848
var state: State
49+
var isDisposed = false
3950

4051
init(initialState: State) {
4152
state = initialState

Tests/HooksTests/Hook/UseEffectTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ final class UseEffectTests: XCTestCase {
9595

9696
tester.update()
9797
tester.dispose()
98+
9899
XCTAssertEqual(cleanupCount, 2)
99100
}
100101

Tests/HooksTests/Hook/UsePublisherSubscribeTests.swift

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,22 +57,28 @@ final class UsePublisherSubscribeTests: XCTestCase {
5757
}
5858

5959
func testDispose() {
60+
var isSubscribed = false
6061
let subject = PassthroughSubject<Int, Never>()
6162
let tester = HookTester {
6263
usePublisherSubscribe {
63-
subject
64+
subject.handleEvents(receiveSubscription: { _ in
65+
isSubscribed = true
66+
})
6467
}
6568
}
6669

6770
XCTAssertEqual(tester.value.phase, .pending)
6871

69-
tester.value.subscribe()
70-
71-
XCTAssertEqual(tester.value.phase, .running)
72-
7372
tester.dispose()
7473
subject.send(1)
7574

76-
XCTAssertEqual(tester.value.phase, .running)
75+
XCTAssertEqual(tester.value.phase, .pending)
76+
XCTAssertFalse(isSubscribed)
77+
78+
tester.value.subscribe()
79+
subject.send(2)
80+
81+
XCTAssertEqual(tester.value.phase, .pending)
82+
XCTAssertFalse(isSubscribed)
7783
}
7884
}

Tests/HooksTests/Hook/UsePublisherTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ final class UsePublisherTests: XCTestCase {
104104
func testDispose() {
105105
let subject = PassthroughSubject<Int, Never>()
106106
let tester = HookTester {
107-
usePublisher(.once) {
107+
usePublisher(.always) {
108108
subject
109109
}
110110
}

Tests/HooksTests/Hook/UseReducerTests.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,23 @@ final class UseReducerTests: XCTestCase {
4949

5050
XCTAssertEqual(tester.value.state, 0)
5151
}
52+
53+
func testDispose() {
54+
var isReduced = false
55+
56+
func reducer(state: Int, action: Int) -> Int {
57+
isReduced = true
58+
return state + action
59+
}
60+
61+
let tester = HookTester {
62+
useReducer(reducer, initialState: 0)
63+
}
64+
65+
tester.dispose()
66+
tester.value.dispatch(1)
67+
68+
XCTAssertEqual(tester.value.state, 0)
69+
XCTAssertFalse(isReduced)
70+
}
5271
}

Tests/HooksTests/Hook/UseStateTests.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,15 @@ final class UseStateTests: XCTestCase {
4141

4242
XCTAssertEqual(tester.value.wrappedValue, 0)
4343
}
44+
45+
func testDispose() {
46+
let tester = HookTester {
47+
useState(0)
48+
}
49+
50+
tester.dispose()
51+
tester.value.wrappedValue = 1
52+
53+
XCTAssertEqual(tester.value.wrappedValue, 0)
54+
}
4455
}

0 commit comments

Comments
 (0)