왜 나온걸까?
Composable 함수는 Tree구조를 가지고 있다.
이때, 상태(state)는 일반적으로 최상단 노드에 저장이 되어있어야 한다(상태 호이스팅)
상위 트리에서 만든 상태를 하위 트리에서 매개변수로 받는 것이 아닌, 바로 접근할 수 있게 해준다.
왜 최상단에 저장되어있어야 할까?
컴포넌트들은 상태를 모르는게 좋다. 최상단에서 전달해준 정보를 바탕으로 화면을 나타내는 것이 더 좋은 방법이라고 생각한다. 왜냐하면 상태를 컴포넌트 별로 다 가지고 있다면 이후 추적하는 것도 힘들고, 각각의 상태를 하나씩 참조하며 변경해줘야 하기 때문에 관리하기 더 어려워 진다.
그렇다면 상태가 최상단에 있고, 트리 개념으로 아래로 내려가는 UI 구조를 가진다고 했는데 “매번 변수로 state를 내려줘야 하면 너무 귀찮은거 아냐?” 라는 생각이 들 수 있다.
그래서 나온 개념이 CompositionLocal이다.
어떻게 생성할까?
CompositionLocal은 상태를 제공하는 일종의 컨테이너와 같다. CompositionLocal을 만드는 2가지 방법은 다음과 같다.
// StaticProvidableCompositionLocal로 만들기
val localStaticComposition = staticCompositionLocalOf {
DEFAULT_VALUE
}
// DynamicProvidableCompositionLocal로 만들기
val localDynamicComposition = compositionLocalOf {
DEFAULT_VALUE
}
어떻게 하위로 제공할까?
상위 컴포저블 함수부터 하위까지 CompositionLocal을 제공하기 위해 다음과 같이 CompositionLocalProvider를 사용한다.
val LocalErrorComposition = staticCompositionLocalOf<String> {
"ERROR"
}
@Composable
fun TopComposable() {
..
CompositionLocalProvider(LocalErrorComposition provide "String") {
TailComposable()
}
}
@Composable
fun TailComposable() {
..
}
CompositionLocalProvider 함수 내부를 살펴보면 알겠지만, CompositionLocal은 1개 이상 제공할 수 있다.
fun CompositionLocalProvider(
vararg values: ProvidedValue<*>,
content: @Composable () -> Unit)
어떻게 값을 받아올까?
CompositionLocalProvider로 감싼 하위 컴포저블함수들은 이제 CompositionLocal로부터 현재 값을 읽을 수 있다.
@Composable
fun TailComposable() {
Text(
modifier = Modifier,
text = LocalErrorComposition.current
)
...
}
staticCompositionLocalOf()와 compositionLocalOf()는 뭐가 다를까?
staticCompositionLocalOf() : 자주 변경되지 않는 상태를 저장할 때 이용하면 좋다. 상태가 변경되면 해당 상태가 할당된 노드의 하위 노드를 모두 재구성해야하기 때문이다.
compositionLocalOf()
: 현재 상태에 접근하는 컴포저블에 대해서만 재구성을 수행한다. 이 함수는 변경이 잦은 상태를 다룰 때 이용해야 한다.
이렇게만 설명하면 무조건 compositonLocalOf가 좋아보인다.
compositionLocalOf()
와 달리 staticCompositionLocalOf()
읽기는 Compose에서 추적하지 않는다.
값을 변경하면 컴포지션에서 current 값을 읽는 위치만이 아니라 CompositionLocal이 제공된 content 람다 전체가 재구성된다.
즉, CompositionLocal에 저장한 값이 변경되지 않을 값이라면 성능이 더 좋은staticCompostionLocalOf
를 사용하는것이 좋다.