[안드로이드 Compose 2] - State, State Hoisting

이영준·2023년 8월 23일
0

안드로이드_Compose

목록 보기
2/6

📌 State

어떠한 값에 따라 화면을 다르게 렌더링 하는 뷰가 있다. 클릭에 따라서 그 값이 바뀌면 화면이 달라져야 한다.
그러나 새로 렌더링을 하면서 다시 초기에 설정한 값이 렌더링되기 때문에 바뀐 값이 저장되어야 한다.

따라서 재 렌더링에도 상태를 저장해야 하는 경우에는 mutableStateOf를 사용한다.

🔑 mutableStateOf()

var expanded by remember {
        mutableStateOf(false)
    }
var extraPadding = if(expanded) 48.dp else 0.dp
...
Column(modifier = Modifier.padding(bottom = extraPadding)) {
                Text(text = "Hello,")
                Text(text = name)
            }
    

🔑 remember && rememberSavable

remember로 state를 감싸서 변경가능한 상태를 기억해야 한다. 화면 회전에도 대응해야 되는 경우에는 rememberSavable로 설정한다.

참고로 mutablestate의 value인 불 값을 expanded.value가 아닌 그냥 expanded 로 가져오기 위해
property delegation by를 사용한다. 위처럼 선언하고 import를 해주면 value를 가져오는 setter, getter가 알아서 만들어져 expanded 키워드 자체로 value를 가져올 수 있다.

전체 컴포넌트 코드

@Composable
fun Greeting3(name: String) {
    var expanded by rememberSavable {
        mutableStateOf(false)
    }
    var extraPadding = if (expanded) 48.dp else 0.dp
    Surface(color = MaterialTheme.colors.secondary, modifier = Modifier.padding(bottom = 4.dp)) {
        Row(
            modifier = Modifier
                .padding(24.dp)
                .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween
        ) {
            Column(modifier = Modifier.padding(bottom = extraPadding)) {
                Text(text = "Hello,")
                Text(text = name)
            }
            Button(onClick = {
                expanded = !expanded

            }) {
                Text(if (expanded) "show less" else "show more")
            }
        }
    }
}

.verticalScroll(rememberScrollState())
는 스크롤의 상태를 state로 저장한다. 기본적으로 compose는 scrollable하게 되지 않기 때문에
verticalScroll 등의 속성을 지정해준다.

📌 lazyColumn

    LazyColumn( modifier = Modifier
        .fillMaxSize()
        .background(color = Color.Green)
//        .verticalScroll(rememberScrollState())
    ){
        items(50){
            Log.d(TAG, "Greeting4: $it")
            Text("name : $it")
        }
    }

lazycolumn은 item이 화면에 렌더링이 될 때 생성해준다. verticalScroll을 달지 않아도 스크롤이 된다.

🔑 Context 가져오기 - LocalContext.current

위치한 activity의 context를 가져오는 키워드는 LocalContext.current입니다.

📌 구조분해 할당

@Composable
fun Greeting6(name: String) {
    val textValue = remember {
        mutableStateOf("")
    }
    val context = LocalContext.current
    Column(modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        TextField(value = textValue.value, onValueChange = {
            textValue.value = it
        })
        Spacer(modifier = Modifier.padding(10.dp))
        Button(onClick = {
            Toast.makeText(context, "${textValue.value}", Toast.LENGTH_SHORT).show()
        }) {
            Text("click")
        }
    }
}

원래는 위와 같은 방식의 코드로 사용할 수 있지만

@Composable
fun Greeting6(name: String) {
    val (a, b) = remember { //a가 get b가 set
        mutableStateOf("")
    }
    val context = LocalContext.current
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        TextField(value = a, onValueChange = {
            b (it)
        })
        Spacer(modifier = Modifier.padding(10.dp))
        Button(onClick = {
            Toast.makeText(context, "${a}", Toast.LENGTH_SHORT).show()
        }) {
            Text("click")
        }
    }
}

mutableState는 Component1이 getValue, Component2가 setValue로 설정되어있으며 이를 바로 구조분해 할당해줄 수 있다. 따라서 위와같이 사용할 수도 있다.

📌 State Hoisting

State 관리를 뷰 단에서 하는게 아닌 밖으로 뺄 수 있으면 viewModel에 이를 넣거나 하는 등 관리에 수월할 것이다.

@Composable
fun TextFieldStateChange(){
    var fieldVal by remember {
        mutableStateOf("")
    }
    TextFieldOutlinedStateLess(fieldVal = fieldVal, onChange = {
        fieldVal = it
    })
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TextFieldOutlinedStateLess(fieldVal : String, onChange : (String) -> Unit){
    OutlinedTextField(value = fieldVal, onValueChange = onChange)
}

실제로 그려지는 TextFieldOutlinedStateLess에서는 state에 대한 참조가 없다.
대신에 onChange 라는 함수를 받는데 이 함수가 stated를 바꾸는 작업을 하기 때문에 (onChange = { fieldVal = it })

state가 변화되고, TextFieldStateChange() 내부의 TextFieldOutlinedStateLess까지 같이 바뀌는 것이다.

profile
컴퓨터와 교육 그사이 어딘가

0개의 댓글