어제 쓴 게 좀 그래서 좀 더 살펴보니 useReducer라는게 있었지 참! 싶어서 그걸로 고쳤다.
package utils
import react.*
external interface ReducerAction<TActionType> where TActionType : Enum<TActionType> {
var type: TActionType
var value: Any?
}
abstract class UseReducer<TState, TActionType>(private val initialState: TState) where TState : State, TActionType : Enum<TActionType> {
private val reducerContext = createContext<ReducerInstance<TState, ReducerAction<TActionType>>>()
val provider = FC<PropsWithChildren> { props ->
reducerContext.Provider {
value = useReducer(this@UseReducer::reduce, initialState)
+props.children
}
}
protected abstract fun reduce(state: TState, action: ReducerAction<TActionType>): TState
fun useReducerInstance(): ReducerInstance<TState, ReducerAction<TActionType>> {
return requireNotNull(useContext(reducerContext)) {
"Context is not initialized"
}
}
fun useState(): TState = requireNotNull(useContext(reducerContext)).component1()
fun <U> useSelector(selector: (TState) -> U): U = requireNotNull(useContext(reducerContext)) {
"Context is not initialized"
}.component1().let(selector)
fun useDispatch(): Dispatch<ReducerAction<TActionType>> = requireNotNull(useContext(reducerContext)) {
"Context is not initialized"
}.component2()
}
처음에 ReducerAction을 data class 로 했었는데 에러가 나서 external interface로 바꿨다. data class를 자바스크립트로 트랜스파일 했을 때 문제가 일어나는 듯 하다.
package states
import js.objects.jso
import react.State
import utils.ReducerAction
import utils.UseReducer
external interface CounterState : State {
var count: Int
}
enum class CounterStateActionType {
INCREASE,
DECREASE,
ADD,
SUBTRACT
}
object Counter : UseReducer<CounterState, CounterStateActionType>(jso { count = 0 }) {
override fun reduce(state: CounterState, action: ReducerAction<CounterStateActionType>): CounterState = when (action.type) {
CounterStateActionType.INCREASE -> jso { count = state.count + 1 }
CounterStateActionType.DECREASE -> jso { count = state.count - 1 }
CounterStateActionType.ADD -> jso {
count = state.count + checkNotNull(action.value as Int) {
"value is null"
}
}
CounterStateActionType.SUBTRACT -> jso {
count = state.count - checkNotNull(action.value as Int) {
"value is null"
}
}
}
}
import js.objects.jso
import react.FC
import react.create
import react.dom.client.createRoot
import react.dom.html.ReactHTML.button
import react.dom.html.ReactHTML.h1
import states.CounterStateActionType
import states.Counter
import web.dom.document
fun main() {
val root = document.getElementById("root")!!
createRoot(root).render(App.create())
}
val App = FC {
Counter.provider {
CountViewer()
Buttons()
}
}
val CountViewer = FC {
val count = Counter.useSelector { it.count }
h1 {
+"Count : $count"
}
}
val Buttons = FC {
val dispatch = Counter.useDispatch()
button {
onClick = {
dispatch(jso {
type = CounterStateActionType.INCREASE
})
}
+"Increase"
}
button {
onClick = {
dispatch(jso {
type = CounterStateActionType.DECREASE
})
}
+"Decrease"
}
button {
onClick = {
dispatch(jso {
type = CounterStateActionType.ADD
value = 5
})
}
+"Add 5"
}
button {
onClick = {
dispatch(jso {
type = CounterStateActionType.SUBTRACT
value = 5
})
}
+"Subtract 5"
}
}
답글 남기기