[ComposeCamp] Week1.2 - basic state

David·2022년 12월 12일
0

[Android] Compose

목록 보기
4/5
post-thumbnail

👀 컴포즈에서의 상태


1. Value Holder

  • 컴포저블이 실행되는 동안 읽을 수 있는 값 홀더

2. Observable

  • Recompose 범위에서 관찰
  • 변화된 값에 구독

3. Recomposition Trigger

  • 값 속성이 기록되고 변경되면 구독된 Recompose 범위의 재구성이 예약됩니다.

👆 컴포즈의 이벤트


앱 상태로 UI에 표시할 항목에 관한 설명이 제공되고,
이벤트라는 메커니즘을 통해 상태가 변경되고 UI도 변경됩니다.

💡 핵심 아이디어: 상태는 존재하고 이벤트는 발생한다.


📸 구성 가능한 함수의 메모리


💡 Compose가 추적할 상태를 알아야 함 > 그래야 업데이트를 받을 때 리컴포지션을 예약할 수 있음

  • 컴포지션 : 컴포저블을 실행할 때 Jetpack Compose에서 빌드한 UI에 관한 설명입니다.
  • 초기 컴포지션: 처음 컴포저블을 실행하여 컴포지션을 만듭니다.
  • 리컴포지션: 데이터가 변경될 때 컴포지션을 업데이트하기 위해 컴포저블을 다시 실행하는 것

State 및 MutableState

  • compose 에서 상태를 관찰할 수 있도록 함.

🌅 Compose에서 상태 복원


  • remember
    • 단일 객체를 컴포지션에 저장하는 메커니즘
    • 리컴포지션 간에 상태를 유지하는 데 도움
    • 구성 변경 간에는 유지되지 않음. (configuration 변경 시)
  • rememberSavable
    • Bundle에 저장할 수 있는 모든 값을 자동으로 저장
    • 다른 값의 경우에는 맞춤 Saver 객체를 전달할 수 있음.

활동 또는 프로세스가 다시 생성된 이후 
rememberSaveable을 사용하여 UI 상태를 복원합니다.
여러 리컴포지션 간에 상태를 유지하는 것 외에도 
rememberSaveable은 활동 및
프로세스 재생성 전반에 걸쳐서도 상태를 유지합니다.


🔺상태 및 호이스팅


📌 스테이트풀(Stateful)

  • remember를 사용하여 객체를 저장하는 컴포저블에는 내부 상태가 포함되어 있는 컴포저블
  • 내부 상태를 갖는 컴포저블은 재사용 가능성이 적고 테스트하기가 더 어려운 경향
  • 상태와 관련된 가장 부모의 컴포저블에서 stateful한 compose 구성

📌 스테이트리스(Stateless)

  • 상태를 보유하지 않는 컴포저블
  • 상태 끌어올리기를 사용(호이스팅) stateless 컴포저블을 쉽게 만듬
  • 만드는 법

    💡 상태 호이스팅 패턴

    두 개의 매개변수(파라미터)로 바꾸는 것

    • value: T - UI에 표시해야 할 현재 값(상태), 상태는 내림(🔻)
    • onValueChange: (T) -> Unit - 값을 변경하도록 요청하는 이벤트, 위임할 액션(🔺)

    💡 단방향 데이터 흐름(UDF)

    • 상태가 내려가고 이벤트가 올라가는 패턴
    • 단방향 데이터 흐름(UDF)
    • 호이스팅을 이용한 Compose 구현하는 방법

✅ 호이스팅(Hoisting)

장점

  • 단일 소스 저장소
    : 상태를 복제하는 대신 옮겼기 때문에 소스 저장소가 하나만 있음.
    : 버그방지에 도움
  • 공유 가능함
    : 끌어올린 상태를 여러 컴포저블과 공유할 수 있음.
  • 가로채기 가능함
    : 스테이트리스(Stateless) 컴포저블의 호출자는 상태를 변경하기 전에
    이벤트를 무시할지 수정할지 결정할 수 있음.
  • 분리됨
    : 구성 가능한 스테이트리스(Stateless) 함수의 상태는 어디에든(예: ViewModel) 저장할 수 있음.

호이스팅 규칙

핵심 사항
상태를 끌어올릴 때 상태의 이동 위치를 쉽게 파악할 수 있는 세 가지 규칙

1. 상태는 적어도 그 상태를 사용하는 모든 컴포저블의 가장 낮은 공통 상위 요소로 끌어올려야 한다. (읽기)
2. 상태는 최소한 변경될 수 있는 가장 높은 수준으로 끌어올려야 한다. (쓰기)
3. 두 상태가 동일한 이벤트에 대한 응답으로 변경되는 경우 두 상태는 동일한 수준으로 끌어올려야 합니다.

상태를 충분히 높은 수준으로 끌어올리지 않으면 단방향 데이터 흐름을 따르거나 어렵거나 불가능할 수 있다.


컴포즈 팁


  • 컴포즈 툴 팁
  • 라이브 템플릿
    • comp: @Composeable 함수 설정
    • prev: 구성 가능한 @Preview 함수 만들기
    • paddp: dp에 padding 수정자 추가
    • weight: weight 수정자 추가
    • W, WR, WC: 현재 컴포저블을 Box, Row 또는 Column 컨테이너로 둘러싸기
  • 목록 스크롤 상태 팁
    • val listState = rememberLazyListState()

관찰 가능한 MutableList


  • mutableStateListOf API 사용하여 목록을 만들 수 있다.
// 💩 Don't do this!
// 리컴포지션 UI 성능 최적화 X
val list = remember { mutableStateListOf<WellnessTask>() }
list.addAll(getWellnessTasks())
// ✅ Do this instead. 
// 단일 작업으로 초기값을 사용하여 목록 만든 후 remember 함수에 전달
val list = remember { 
	mutableStateListOf<WellnessTask>().apply {
       addAll(getWellnessTasks())
    }
}

Column or Row 에서 list 사용 시 고려할 점

  • 변경 가능한 목록에서 데이트 세트가 변경될 때
    --> 위치를 변경하는 항목은 기억된 상태를 잃음
    --> 데이터들이 삭제 추가 됐을 때 기본 상태를 저장할 시 위치로 저장함
    --> 따라서 데이트 크기가 변경 될 때 문제 발생

  • items 메서드는 key 매개변수를 수신.
    --> 모든 데이터를 리컴포지션 할 필요 없어짐
    --> 단 키는 반드시 유니크 해야함 > 현실적으로 어려울 수 있음 고려
    --> 예) items(key = {task -> task.id})


ViewModel의 상태


  • viewModel() 함수를 호출하여 컴포저블에서 이 viewModel에 엑세스할 수 있음.
    	> implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1"
  • viewModel()은 기존 ViewModel을 반환하거나
    지정된 범위에서 새 ViewModel을 생성합니다.
    ViewModel 인스턴스는 범위가 활성화되어 있는 동안 유지됩니다.
    예를 들어 컴포저블이 활동에서 사용되는 경우 
    viewModel()은 활동이 완료되거나 프로세스가 종료될 때까지
    동일한 인스턴스를 반환합니다.

ViewModel은 탐색 그래프의 활동이나 프래그먼트, 대상에서 호출되는 루트 컴포저블에 가까운 화면 수준 컴포저블에서 사용하는 것이 좋습니다. ViewModel은 다른 컴포저블로 전달하면 안 됩니다. 대신 필요한 데이터와 필수 로직을 실행하는 함수만 매개변수로 전달해야 합니다.
자세한 내용은 ViewModel 및 상태 홀더 섹션과 Compose 및 기타 라이브러리 문서를 참고하세요.


추적 가능한 데이터 클래스


// 추적 안 되는 클래스
data class WellnessTask(
    val id: Int, 
    val label: String,
    var checked: Boolean = false // Compose 에서 해당 값을 추적이 안 됨
)

개선1 - MutableState로 속성넣기

  • 해당 클래스의 checked.value로 set&get 접근해야하는 불편함 존재
data class WellnessTask(
    val id: Int, 
    val label: String,
    val checked: MutableState<Boolean> = mutableStateOf(false) // Compose 에서 해당 값 추적 가능 함
)

개선2 - 위임 속성 활용

class WellnessTask(
    val id: Int, 
    val label: String,
    initialChecked: Boolean = false
) {
   var checked by mutableStateOf(initialChecked)
}
profile
공부하는 개발자

0개의 댓글