[Jetpack Compose] 3. Architecture and state(1) #State holder

0

Jetpack Compose

목록 보기
10/11
post-thumbnail
post-custom-banner

[Jetpack Compose] 3. Architecture and state(1)

📌참고자료

Component Phases

  • Compose에서 Data를 UI로 transform하는 3 단계
    • (1) Composition: What to show
      • compose runtime executes composable function -> outputs a tree data structure that represents the UI
      • elements in tree = layout node
      • layout node holds all the information needed
    • (2) Layout: Where to place
      • each layout node measures its children
        -> decides its own size (=> height & width)
        -> place them in 2D space (=> x 좌표 & y 좌표)
    • (3) Drawing: How to render
      • each layout node draw pixels on screen

A Compose state of mind

  • Types of state and logic
    • UI-element state: hoisted state of UI elements
    • Screen/UI state: what to display on screen
      • contains application data
      • usually connected with other layers
    • UI behavior logic: how to display state changes
      • ex. navigation, snackbars
      • should live in the composition
    • Business logic: what to do with state changes
      • placed in business/data layer, never in UI layer

Managing state

  • Composables
    • 간단한 UI-element state 관리에 사용
    • single source of truth principle
@Composable
fun JetsnackApp(){
	// place UI-element state in composable
	val scaffoldState = rememberScaffoldState()
    val coroutineScope = rememberCoroutineScope()
    
    // all interactions with scaffoldState should happen in this composable
    
    JetsnackScaffold(scaffoldState = scaffoldState){
    	Content(
        	//...
        )
    }
}
  • 앱이 복잡해지면, seperation of concerns principle을 따라
    delegate UI logic & UI state to different class(= State holder)

  • State holder

    • Composable 내부에서 생성되고 remember되는 plain class -> 생성된 Composable의 lifecycle을 따름
    • 복잡한 UI-element state 관리에 사용
    • 모든 state 변경은 State holder 클래스 내부에서 일어나야 함
    • state 변경될 경우 recomposition되는 composable function 포함 가능
// State holder class
class JetsnackAppState(
	val scaffoldState: ScaffoldState,
    val navController: NavHostController,
    //...
){
	val shouldShowBottomBar: Boolean
    	@Composable get() = // read causes recompositions when value changes
        	navController.currentVackStackEntryAsState().value?.destination?.route in bottomBarRoutes
}
	//...
}
  • State holder를 사용하는 composable은
    -> UI-element state 사용을 위해 State holder 객체의 프로퍼티 사용
    -> UI-element state 변경을 위해 State holder 객체의 메서드 호출
  • State holder를 사용하는 composable은 State holder를 remember 해야
@Composable
fun rememberJetsnackAppState(
	scaffoldState: ScaffoldState = rememberScaffoldState(),
    navController: NavHostController = rememberNavController(),
    //...
) = remember(scaffoldState, navController, ...){
	JetsnackAppState(scaffoldState, navController, ...)
}
  • ViewModel
    • 비즈니스 로직 관리 & 화면 state 관리
    • (1) provide access to business logic that is usually placed in other layers(ex. repositories, use cases)
    • (2) prepare application data for presentation for a particular screen
class CartViewModel(
	private val repository: SnackRepository,
    private val savedState: SavedStateHandle
): ViewModel(){
	fun increaseSnackCount(snackId: Long){ ... }
	fun decreaseSnackCount(snackId: Long){ ... }
    fun removeSnack(snackId: Long){ ... }
    fun completeOrder(){ ... }
}
  • ViewModel is not part of the composition
    • has longer lifecycle than the composition
    • should not take state created in composables
  • State holder can take ViewModel as dependency

Streams of data in Compose

  • Streams of data를 Compose의 observable state로 변환하는 helper functions 존재
  • 예. Compose에서 collectAsState 함수 사용해 Flow 사용
    • flow에서 새 value가 emitted 될 때마다 recomposition 발생
class CartViewModel(
	private val repository: SnackRepository,
    private val savedState: SavedStateHandle
): ViewModel(){
	val snacks: Flow<List<Snack>> = repository.snacks
    
	//...
}
@Composable
fun Cart(viewModel: CartViewModel = viewModel()){
	val snacks by viewModel.snacks.collectAsState()
    //...
}

Where to hoist your state

  • Not hoisting is a valid option
  • place state in the lowest common ancestor
profile
Be able to be vulnerable, in search of truth
post-custom-banner

0개의 댓글