Last active
January 23, 2018 03:09
-
-
Save Arthraim/e9b142118fd2dc3318320d9536944ca4 to your computer and use it in GitHub Desktop.
An extremely easy implementation of redux in Swift 4
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: An extremely easy implementation of redux in Swift 4 | |
// !!!! | |
// THIS IMPLEMENTATION HAS BEEN MOVED TO https://github.com/Arthraim/redux.swift | |
// !!!! | |
typealias Reducer<S, A> = (_ state: S, _ action: A) -> S | |
typealias Listener = () -> Void | |
class Store<S, A> { | |
var currentReducer: Reducer<S, A> | |
var currentState: S | |
var currentListeners: [Int:Listener] | |
var nextListeners: [Int:Listener] | |
var isDispatching: Bool | |
init(_ reducer: @escaping Reducer<S, A>, preloadedState: S){ | |
currentReducer = reducer | |
currentState = preloadedState | |
currentListeners = [:] | |
nextListeners = currentListeners | |
isDispatching = false | |
} | |
func getState() -> S { | |
guard !isDispatching else { | |
fatalError( | |
"You may not call store.getState() while the reducer is executing. " + | |
"The reducer has already received the state as an argument. " + | |
"Pass it down from the top reducer instead of reading it from the store." | |
) | |
} | |
return currentState | |
} | |
func subscribe(listener: @escaping Listener) -> () -> Void { | |
guard !isDispatching else { | |
fatalError( | |
"You may not call store.subscribe() while the reducer is executing. " + | |
"If you would like to be notified after the store has been updated, subscribe from a " + | |
"component and invoke store.getState() in the callback to access the latest state. " | |
) | |
} | |
var isSubscribed = true | |
let key = Int(arc4random_uniform(UInt32(6))) | |
nextListeners.updateValue(listener, forKey: key) | |
return { | |
guard isSubscribed else { return } | |
guard !self.isDispatching else { | |
fatalError("You may not unsubscribe from a store listener while the reducer is executing. ") | |
} | |
isSubscribed = false | |
self.nextListeners.removeValue(forKey: key) | |
} | |
} | |
func dispatch(action: A) -> A { | |
guard !isDispatching else { | |
fatalError("Reducers may not dispatch actions.") | |
} | |
isDispatching = true | |
currentState = currentReducer(currentState, action) | |
isDispatching = false | |
currentListeners = nextListeners | |
for listener in currentListeners.values { | |
listener() | |
} | |
return action | |
} | |
} | |
enum CounterAction { | |
case inc(num: Int) | |
case dec(num: Int) | |
} | |
typealias CounterState = Int | |
func counter(_ state: CounterState, _ action: CounterAction) -> CounterState { | |
switch action { | |
case .inc(let i): | |
return state + i | |
case .dec(let i): | |
return state - i | |
} | |
} | |
let store = Store(counter, preloadedState: 0) | |
func render() { | |
let state = store.getState() | |
print(state) | |
} | |
let unsubscribe = store.subscribe(listener: render) | |
store.dispatch(action: .inc(num: 1)) | |
store.dispatch(action: .inc(num: 1)) | |
store.dispatch(action: .inc(num: 2)) | |
store.dispatch(action: .inc(num: 1)) | |
store.dispatch(action: .dec(num: 1)) | |
store.dispatch(action: .dec(num: 3)) | |
unsubscribe() | |
store.dispatch(action: .inc(num: 3)) |
另外通过记 index 来 unsubscribe 也比较危险,如果中间的 listener unsubscribe 了,后面的 index 应该就变了?
理想情况可能还是 Listener 能够传入自己来找到自己?
@luosky 更新啦,解决啦
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
isSubscribed 应该要 nextListeners.count 为 0 时才设为 false?
而因此其实也不需要一个变量来存储 isSubscribed,直接用一个方法返回 nextListeners.count != 0 即可?