Android(kotlin) - JetPack Compose - State & Hoisting

하동혁 ·2023년 9월 1일
0

Android Jetpack Compose

목록 보기
29/30
post-thumbnail

remember

Composable 함수 내에서 상태를 유지하기 위해 사용됩니다. 이 함수로 생성된 상태는 Composable 함수가 재호출되더라도 변경되지 않습니다. 즉, 상태가 Composable 함수가 호출될 때 한 번 생성되고 그 상태가 유지됩니다. 이는 재호출이 발생하더라도 상태를 유지하고 싶을 때 사용됩니다.

rememberSaveable

주로 화면 회전과 같은 상태 변경으로 인해 Composable 함수가 다시 생성될 때 상태를 보존하고 복원하기 위해 사용됩니다. 이 함수를 사용하면 Composable 함수가 재생성되더라도 상태를 유지하며, 이전에 입력된 데이터나 상태를 회복하는 데에 특히 유용합니다.

실습 [1]

  • 첫 번째 실습은 OutlinedTextField 내부까지 값이 도달하게 했습니다.
    이러한 방식은 권장하지 않은 방식입니다.
  • 가능한 상태가 전달되는 범위를 좁히는 것이 중요합니다.
    두 번째 실습에서는 State Hoisting을 활용하여 상태 전달 범위를 좁히는 실습을 진행하겠습니다.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun StateHoistingEx() {
    /*
        rememberSaveable은 Configuration이 변경시 값을 저장할 수 있다.
        그러나 캐시를 사용하기 때문에 많은 데이터에 사용하는 것은 권장하지 않는다.
     */

    var num by rememberSaveable {
        mutableStateOf("0")
    }
    var result by remember {
        mutableStateOf((0.0).toString())
    }

    Column(modifier = Modifier.padding(16.dp)) {
        OutlinedTextField(
            value = num,
            onValueChange = {
                if(it.isBlank()) {
                    num = ""
                    result = ""
                    return@OutlinedTextField
                }
                val inputNum = it.toFloatOrNull() ?: return@OutlinedTextField // toFloatOrNull() : Float형으로 변경 불가능한 것은 null 리턴
                result = (inputNum * inputNum * 3.14).toString()
                num = it

            }, label = {
                Text("반지름")
            }
        )
        OutlinedTextField(
            value = result,
            onValueChange = {
                result = it
            },
            label = {
                Text("원 넓이")
            }
        )
    }
}

@Preview(showBackground = true)
@Composable
fun StateHoistingPreview() {
    Compose_exampleTheme {
        StateHoistingEx()
    }
}



실습 [2] - State Hoisting

  • State Hoisting을 사용하여 상태를 관리합니다.
  • 가능한 상태를 별도로 빼서 관리하는 것이 좋습니다.
    ( == UI 부분은 Stateless하게 관리하는 것이 좋다.)
  • 자세한 설명은 주석을 참고
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun StateHoistingEx() {
    /*
        rememberSaveable은 Configuration이 변경시 값을 저장할 수 있다.
        그러나 캐시를 사용하기 때문에 많은 데이터에 사용하는 것은 권장하지 않는다.
     */

    var num by rememberSaveable {
        mutableStateOf("0")
    }
    var resultNum by remember {
        mutableStateOf((0.0).toString())
    }

    numToResultStateless(
        num,
        resultNum
    ) {
        if (it.isBlank()) {
            num = ""
            resultNum = ""
            return@numToResultStateless  // 입력 값이 numToResultStateless에서 리턴되기 때문
        }
        val inputNum = it.toFloatOrNull()
            ?: return@numToResultStateless // toFloatOrNull() : Float형으로 변경 불가능한 것은 null 리턴
        resultNum = (inputNum * inputNum * 3.14).toString()
        num = it
    }

}

/*
    numToResultStateless는 상태를 가지고 있지 않아서 상태를 변경하지 않는다.
    onNumChange: (String) -> Unit 은 num이 변경됬을때 호출할 콜백을 사용해 상위 계층에서 상태를 변경하도록 한다.
    이 방식을 통해 numToResultStateless는 상태에 개입하지 않도록 한다.
    State관리의 책임은 StateHoistingEx()에서 맡도록 한다.
 */
@Composable
fun numToResultStateless(
    num: String,
    resultNum: String,
    onNumChange: (String) -> Unit
) {
    Column(modifier = Modifier.padding(16.dp)) {
        OutlinedTextField(
            value = num,
            onValueChange = onNumChange, // 상태 변경을 onNumChange로 위임
            label = {
                Text("반지름")
            }
        )
        OutlinedTextField(
            value = resultNum,
            onValueChange = {},
            label = {
                Text("원 넓이")
            }
        )
    }
}

@Preview(showBackground = true)
@Composable
fun StateHoistingPreview() {
    Compose_exampleTheme {
        StateHoistingEx()
    }
}
  • 왜 State Hoisting을 해서 상태가 없도록 하는가?
    • 테스트시 위 예시 코드와 같이 onNumChange: (String) -> Unit를 변형시켜 손 쉽게 테스트가 가능
    • 첫 번째 예시에서 한 곳에 numresultNum를 연동할 필요가 사라짐
    • 두 번째 예시 코드에서 fun numToResultStateless()는 상태가 없다.
      ⇒ 여러개의 numToResultStateless를 같이 호출할 수 있다. 즉, 상태를 한 곳에 모아둘 수 있다.
  • 두 번째 예시 코드는 상태를 StateHoistingEx()서 관리하도록 했습니다.
    이것을 ViewModel 계층으로 옮길 수 있습니다.
    (numToResultStateless는 변경이 필요 X)

0개의 댓글