
상태 객체를 생성하고 매 컴포지션마다 값을 업데이트하여 최신화하는 함수
@Composable
fun <T> rememberUpdatedState(newValue: T): State<T> = remember {
mutableStateOf(newValue)
}.apply { value = newValue }
이름과 설명만 보면 remember { mutableStateOf } 도 있고, 참조한 상태가 변했을때만 계산하는 derivedStateOf 도 존재하는데 왜 이걸 사용할까 의문이 들 수도 있다.
하지만 함수가 있는 것에는 다 이유가 있는법.
우선 derivedStateOf 의 경우 N 개의 상태를 통해 새로운 상태를 파생 하므로 이 함수의 목적과는 다르다.
그나마 비슷한 것은 remember { mutableStateOf() } 인데 언뜻보면 이 둘은 서로 같다고 볼 수 있으나, 사용 방법은 아예 다르다.
우리는 버튼을 눌러 count 를 1씩 올리고, Child 에서 이 값이 2로 나눠지면 Text 에 Boolean 값을 보여주려고 한다.
이것을 코드로 나타내면 다음과 같다.
class MainActivity() : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
enableEdgeToEdge()
MaterialTheme {
Scaffold {
Column(modifier = Modifier
.fillMaxSize()
.padding(it)
) {
var count by remember { mutableStateOf(1) }
Child(count)
Button({ count++ }) { }
}
}
}
}
}
}
@Composable
fun Child(count: Int){
val isEven by remember(count) { mutableStateOf(count % 2 == 0) }
Text(text = isEven.toString())
}
Child 에서 받는 count 는 순수한 값 자체이므로 key 를 지정해주지 않으면 isEven 은 리컴포지션 시에도 inValid 가 false 가 되어 재계산이 진행되지 않는다.
그렇기에 우리는 key 에 count 를 넣어줬는데, 이렇게 되면 count 가 변화할 때마다 mutableStateOf 함수가 실행되어 새로운 State 인스턴스를 생성한다.
만약 count 가 1만번 증가했다면 1만번의 새로운 인스턴스가 생성되는 것이다.
그런데 만약 위의 코드에서 변화가 생겨 다음과 같이 LaunchedEffect 를 사용한다고 생각해보자.
@Composable
fun Child(count: Int){
val isEven by remember(count) { mutableStateOf(count % 2 == 0) }
Text(text = isEven.toString())
LaunchedEffect(Unit) {
while (true){
delay(500L)
Timber.d("확인 - ${isEven}")
}
}
}
LaunchedEffect 의 경우 key 가 변경되지 않으면 내부 코루틴은 재시작하지 않는다.
그런데 만약 이전에 말한것처럼 remember 의 key 바뀌어 새 인스턴스가 생성된다면 어떻게 될까?
LaunchedEffect 가 바라보는 State Instance 와 현재 State Instance 가 다르기 때문에 값의 추적이 불가능해진다.
이럴 때 다음과 같이 수정해준다면 값을 최신화하여도 추적이 가능해진다.
@Composable
fun Child(count: Int){
val isEven by rememberUpdatedState(count % 2 == 0)
Text(text = isEven.toString())
LaunchedEffect(Unit) {
while (true){
delay(500L)
Timber.d("확인 - ${isEven}")
}
}
}
이게 가능한 이유는 rememberUpdatedState 가 내부적으로
remember {
mutableStateOf(newValue)
}.apply { value = newValue }
와 같이 key 를 지정하지 않는, 기존 인스턴스에 대한 값 업데이트만을 진행하기 때문이다.
매우 편리해보일 수 있으나, 이것은 변수를 읽기 상태로 만들어줘야하고, 내부 코드가 remember { mutableStateOf }.apply { value = newValue } 이기 때문에 매 컴포지션 (함수 호출) 이 발생할 때마다 계산식이 이루어지므로 매우 복잡한 계산식이 사용된다면 이를 권장하지 않는다.