안드로이드 학습(Compose)

상태 호이스팅

리저브콜드브루 2025. 3. 4. 16:08
728x90
반응형

여러 @Compose 함수에서 읽거나 수정하는 상태를 공통 상위 컴포저블로 이동 시키는 것

 

호이스팅 장점

  • 상태 중복을 방지하고 버그를 줄임
  • 컴포저블을 더 쉽게 재사용 가능
  • 테스트 용이

주의점: 상위 요소에서 제어할 필요가 없는 상태는 호이스팅 불필요

 

 

잘못된 예시

...

@Composable
fun OnboardingScreen(modifier: Modifier = Modifier) {
    // 잘못된 상태 저장 예시
    var shouldShowOnboarding by remember { mutableStateOf(true) }

    Column(
        modifier = modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("Welcome to the Basics Codelab!")
        Button(
            modifier = Modifier.padding(vertical = 24.dp),
            onClick = { shouldShowOnboarding = false }
        ) {
            Text("Continue")
        }
    }
}

@Preview(showBackground = true, widthDp = 320, heightDp = 320)
@Composable
fun OnboardingPreview() {
    BasicsCodelabTheme {
        OnboardingScreen()
    }
}
  • shouldShowOnboarding이 함수 내부에 위치하여, 상위 MyApp과 공유가 불가
  • 상태를 MyApp에서 관리하고 이벤트만 전달하도록 변경 필요

 

올바른 호이스팅 적용 예시

@Composable
fun MyApp(modifier: Modifier = Modifier, names: List<String> = listOf("World", "Compose")) {
    var shouldShowOnboarding by remember { mutableStateOf(true) } // MyApp에서 상태 저장, by로 권한 위임

    Surface(modifier) {
        if (shouldShowOnboarding) {
            OnboardingScreen(onContinueClicked = { shouldShowOnboarding = false }) // 이벤트 전달
        } else {
            Greetings()
        }
    }
}

...

@Composable
fun OnboardingScreen(onContinueClicked: () -> Unit,
                     modifier: Modifier = Modifier) {
    Column(
        modifier = modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("Welcome to the Basics Codelab!")
        Button(
            modifier = Modifier.padding(vertical = 24.dp),
            onClick = onContinueClicked //이벤트 적용
        ) {
            Text("Continue")
        }
    }
}

@Preview(showBackground = true, widthDp = 320, heightDp = 320)
@Composable
fun OnboardingPreview() {
    BasicsCodelabTheme {
        OnboardingScreen(onContinueClicked = {})
    }
}
  • shouldShowOnboarding을 MyApp으로 이동: 공통 상태 관리
  • OnboardingScreen에서 직접 상태를 변경않고 콜백 함수(onContinueClicked)를 사용하여 이벤트만 전달

전체 코드

@Composable
fun MyApp(modifier: Modifier = Modifier, names: List<String> = listOf("World", "Compose")) {
    var shouldShowOnboarding by remember { mutableStateOf(true) }

    Surface(modifier) {
        if (shouldShowOnboarding) {
            OnboardingScreen(onContinueClicked = { shouldShowOnboarding = false })
        } else {
            Greetings()
        }
    }
}

@Composable
private fun Greetings(
    modifier: Modifier = Modifier,
    names: List<String> = listOf("World", "Compose")
) {
    Column(modifier = modifier.padding(vertical = 4.dp)) {
        for (name in names) {
            Greeting(name = name)
        }
    }
}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier, names: List<String> = listOf("World", "Compose")) {
    val expanded = remember { mutableStateOf(false) }
    val extraPadding = if (expanded.value) 48.dp else 0.dp

    Surface(
        color = MaterialTheme.colorScheme.primary, 
        modifier = modifier.padding(vertical = 4.dp, horizontal = 8.dp)) {

        Row(modifier = Modifier.padding(24.dp)) {
            Column(modifier = Modifier.weight(1f).padding(bottom = extraPadding)) {
                Text(text = "Hello")
                Text( text = "$name")
            }
            ElevatedButton(onClick = { expanded.value = !expanded.value }) {
                Text(if (expanded.value) "Show less" else "Show more")
            }
        }
    }
}

@Preview(showBackground = true, widthDp = 320)
@Composable
fun GreetingPreview() {
    BasicsCodelabTheme {
        Greetings()
    }
}

@Composable
fun OnboardingScreen(onContinueClicked: () -> Unit,
                     modifier: Modifier = Modifier) {
    Column(
        modifier = modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("Welcome to the Basics Codelab!")
        Button(
            modifier = Modifier.padding(vertical = 24.dp),
            onClick = onContinueClicked
        ) {
            Text("Continue")
        }
    }
}

@Preview(showBackground = true, widthDp = 320, heightDp = 320)
@Composable
fun OnboardingPreview() {
    BasicsCodelabTheme {
        OnboardingScreen(onContinueClicked = {})
    }
}

 

실행 모습

 

 

 

728x90
반응형

'안드로이드 학습(Compose)' 카테고리의 다른 글

기기 회전과 상태 유지  (0) 2025.03.04
LazyColumn, LazyRow의 특징 및 사용  (0) 2025.03.04
Compose에서의 상태  (0) 2025.03.04
열과 행 만들기  (0) 2025.03.04
간단한 버튼 동작 구현해보기  (0) 2025.03.04