선언형 UI인 Compose의 장점은 Stateless하기 때문이다. 하지만 Compose로 앱을 만들다 보면 어쩔 수 없이 Stateful할 때 있는데 이때 상태호이스팅 패턴으로 앱을 만들어 Composable 함수를 Stateless하게 만들어 주는 것이다.
호이스팅이 필요한 경우
즉 상태 호이스팅이란 구성요소를 스테이트리스(Stateless)로 만들기 위해 상태를 다른 함수 위로 옮기는 패턴입니다.
이 패턴이 컴포저블에 적용되는 경우 컴포저블에 매개변수 2개가 추가될 때가 많습니다.
@Composable
fun TipTimeScreen() {
var amountInput by remember { mutableStateOf("") }
val amount = amountInput.toDoubleOrNull() ?: 0.0
val tip = calculateTip(amount)
Column(
modifier = Modifier.padding(32.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = stringResource(R.string.calculate_tip),
fontSize = 24.sp,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Spacer(Modifier.height(16.dp))
EditNumberField(value = amountInput,
onValueChange = { amountInput = it }
)
Spacer(Modifier.height(24.dp))
Text(
text = stringResource(R.string.tip_amount, tip),
modifier = Modifier.align(Alignment.CenterHorizontally),
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
}
}
@Composable
fun EditNumberField(
value: String,
onValueChange: (String) -> Unit
) {
TextField(
value = value,
onValueChange = onValueChange,
label = { Text(stringResource(R.string.cost_of_service)) },
modifier = Modifier.fillMaxWidth(),
singleLine = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
)
}
📍 참고: 실제 앱에서는 컴포저블의 기능에 따라 컴포저블을 100% 스테이트리스(Stateless)로 하는 것은 어려울 수 있습니다. 컴포저블이 가능한 한 적게 상태를 소유하고 적절한 경우 컴포저블의 API에 상태를 노출하여 상태를 호이스팅할 수 있도록 컴포저블을 디자인해야 합니다.