remember
를 사용하여 객체를 저장하는 composable은 내부 상태를 생성하여 composable을 Stateful로 만듭니다. 이는 호출자가 상태를 제어할 필요가 없고 상태를 직접 관리하지 않아도 상태를 사용할 수 있는 경우에 유용합니다. 그러나 내부 상태를 갖는 composable은 재사용 가능성이 적고 테스트하기가 더 어려운 경향이 있습니다.
Stateless composable은 상태를 갖지 않는 composable입니다. Stateless를 달성하는 한 가지 쉬운 방법은 State hoisting을 사용하는 것입니다.
Compose에서 State hoisting(직역하면 상태 끌어올리기)는 composable을 Stateless로 만들기 위해 상태를 composable의 호출자로 옮기는 패턴입니다.
State hoisting를 위한 일반적인 패턴은 상태 변수를 다음 두개의 매개변수로 바꾸는 것 입니다.
value: T
: 표시할 현재 값onValueChange: (T) -> Unit
: T
가 제안된 새 값인 경우 값을 변경하도록 요청하는 이벤트@Composable
fun HelloScreen() {
var name by rememberSaveable { mutableStateOf("") }
HelloContent(name = name, onNameChange = { name = it })
}
@Composable
fun HelloContent(name: String, onNameChange: (String) -> Unit) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "Hello, $name",
modifier = Modifier.padding(bottom = 8.dp),
style = MaterialTheme.typography.h5
)
OutlinedTextField(
value = name,
onValueChange = onNameChange,
label = { Text("Name") }
)
}
}
위 예에서는 HelloContent
에서 name
과 onValueChange
를 추출한 다음, 이러한 항목을 HelloContent
를 호출하는 HelloScreen
composable로 옮깁니다.
위와 같이 수정하게 되면 HelloContent
에서 상태를 끌어올리면 더 쉽게 composable을 추론하고 여러 상황에서 재사용하며 테스트할 수 있습니다. 또한, HelloScreen
을 수정하거나 교체할 경우 HelloContent
의 구현 방식을 변경할 필요가 없습니다.
위 그림과 같이 상태가 내려가고(HelloScreen -> HelloContent
) 이벤트가 올라가는(HelloContent -> HelloScreen
) 패턴을 단방향 데이터 플로우(unidirectional data flow)라고 하고, 이를 따르면 UI에 상태를 표시하는 composable과 상태를 저장하고 변경하는 앱 부분을 서로 분리할 수 있습니다.
위 예와 같이 간단한 상태 끌어올리기는 composable에서 관리가 가능합니다. 그러나 저장해야하는 상태의 양이 많거나 로직이 발생하는 경우 이러한 책임을 State holder에 위임하는 것이 좋습니다.
정리하자면,