Kotlin/JS 상태관리

zustand를 써보려고 했는데 완벽한 래퍼 라이브러리가 없다보니 사용하기 매우 불편하다.

그래서 래퍼를 만들려고 했는데 타입스크립트의 타입추론을 코틀린에서 구현할 수 없어서 포기했다.

그래서 코루틴을 쓰려고 했는데 이게 Kotlin/JS 에서도 정상으로 작동할 지 확인하기 귀찮다.

그래서 object를 이용해서 구현하려 한다.

리액트 Context를 사용하면 자식 컴포넌트들에도 같은 컨텍스트를 제공할 수 있으므로 이걸 사용합시다.

package utils

import react.*

data class StateContextData<TState>(
    var state: TState,
    var setState: StateSetter<TState>
)

abstract class StateContextHolder<TState>(private val key: String) {
    private lateinit var context: Context<StateContextData<TState>>

    fun useProvider(initialState: TState): FC<PropsWithChildren> {
        val (state, setState) = useState(initialState)
        val stateContext = StateContextData(state, setState)
        context = createContext(stateContext)

        return FC { props ->
            context.Provider {
                value = stateContext
                +props.children
            }
        }
    }

    fun useSelector(): TState {
        return useContext(context).state
    }

    fun <U> useSelector(selector: (TState) -> U): U {
        return useContext(context).state.let(selector)
    }

    fun useSetState(): StateSetter<TState> {
        return useContext(context).setState
    }
}

그래서 어떤 식으로 쓸까 예시로는 카운터가 최고지

package states

import utils.StateContextHolder

data class CounterState(val count: Int)

object CounterStateContextHolder : StateContextHolder<CounterState>("Counter")
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.CounterState
import states.CounterStateContextHolder
import web.dom.document

fun main() {
    val root = document.getElementById("root")!!
    createRoot(root).render(App.create())
}

val App = FC {
    val counterStateContextProvider = CounterStateContextHolder.useProvider(CounterState(0))
    counterStateContextProvider {
        CountViewer()
        Buttons()
    }
}

val CountViewer = FC {
    val count = CounterStateContextHolder.useSelector { it.count }

    h1 {
        +"Count : $count"
    }
}

val Buttons = FC {
    val setState = CounterStateContextHolder.useSetState()

    button {
        onClick = {
            setState { state ->
                state.copy(count = state.count + 1)
            }
        }
        +"Increase"
    }
    button {
        onClick = {
            setState { state ->
                state.copy(count = state.count - 1)
            }
        }
        +"Decrease"
    }
}

이렇게 하니 잘 작동한다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다