데이터 모델은 앱의 데이터를 나타냄 UI요소 및 기타 구성요소로부터 독립 앱의 테스트 가능성 & 견고성이 높아짐 관심사 분리 데이터 모델에서 UI 도출 단일소스 저장소 데이터 유형을 정의하는 것을 의미 저장소만 데이터를 수정하거나 변경가능 불변 데이터 노출, 이벤트 수신 및 함수노출로 데이터를 수정 SSOT는 UDF패턴과 함께 사용됨 UDF에서 상태는 한 방향으로만 흐름 데이터를 수정하는 이벤트는 반대방향으로 흐름 https://developer.android.com/jetpack/compose/arc hitecture?hl=ko#udf Single Source Of Truth 단방향 데이터 흐름 (UDF) 기본적인 아키텍처 https://developer.android.com/topic/architecture
& 리컴포지션 시 별도 계산없이 사용 collectAsState collectAsStateWithLifecycle Flow 값을 수집하고 최신 값을 컴포즈 상태로 나타냄 LaunchedEffect 컴포저블 함수 내 코루틴 suspend 함수 실행 rememberUpdateState 할당된 데이터를 remember로 보존 & 값이 들어올때마다 새로운 value Emit StateHolder 많은 양의 State들을 한 곳에 모아 관리할때 사용 DisposableEffect Composition에서 Composable 함수가 끝날 때 호출되는 Callback 함수 DerivedStateOf 다른 상태로부터 파생된 State를 구할때 사용 APIs
& 리컴포지션 시 별도 계산없이 사용 collectAsState collectAsStateWithLifecycle Flow 값을 수집하고 최신 값을 컴포즈 상태로 나타냄 LaunchedEffect 컴포저블 함수 내 코루틴 suspend 함수 실행 rememberUpdateState 할당된 데이터를 remember로 보존 & 값이 들어올때마다 새로운 value Emit StateHolder 많은 양의 State들을 한 곳에 모아 관리할때 사용 DisposableEffect Composition에서 Composable 함수가 끝날 때 호출되는 Callback 함수 DerivedStateOf 다른 상태로부터 파생된 State를 구할때 사용 APIs
<T> remember(crossinline calculation: @DisallowComposableCalls () -> T): T = currentComposer.cache(false, calculation) @ComposeCompilerApi inline fun <T> Composer.cache(invalid: Boolean, block: @DisallowComposableCalls () -> T): T { @Suppress("UNCHECKED_CAST") return rememberedValue().let { if (invalid || it === Composer.Empty) { val value = block() updateRememberedValue(value) value } else it } as T } @Composable inline fun <T> remember( key1: Any?, crossinline calculation: @DisallowComposableCalls () -> T ): T { return currentComposer.cache(currentComposer.changed(key1), calculation) }
-> Unit ) { val applyContext = currentComposer.applyCoroutineContext remember(key1) { LaunchedEffectImpl(applyContext, block) } } 컴포지션 종료시 코루틴도 종료 @Composable내 코루틴을 사용할때 주로 사용 다양한 수의 키를 매개변수로 사용, 키값 변경 시 재실행
by mutableStateOf(initialText) private set fun updateText(newText: String) { text = newText } val isHint: Boolean get() = text == hint } // StateHolder 만들기 1. text는 변경 가능한 상태이므로 상태값을 저장하고 리컴포지션 시 최신 상태값으로 가져오기 위해 mutableStateOf 사용 2. updateText로 상태값 update 3. initialText 매개변수로 text 초기화 4. text의 Hint여부 로직 포함
by mutableStateOf(initialText) private set fun updateText(newText: String) { text = newText } val isHint: Boolean get() = text == hint } // StateHolder 만들기 @Composable fun rememberEditableUserInputState(hint: String): EditableUserInputState = remember(hint) { EditableUserInputState(hint, hint) }
by mutableStateOf(initialText) private set fun updateText(newText: String) { text = newText } val isHint: Boolean get() = text == hint } // StateHolder 만들기 @Composable fun rememberEditableUserInputState(hint: String): EditableUserInputState = remember(hint) { EditableUserInputState(hint, hint) } var selectedCity = rememberSaveable(stateSaver = CitySaver) { mutableStateOf(City("Madrid", "Spain")) }
by mutableStateOf(initialText) private set fun updateText(newText: String) { text = newText } val isHint: Boolean get() = text == hint companion object { val Saver: Saver<EditableUserInputState, *> = listSaver( save = { listOf(it.hint, it.text) }, restore = { EditableUserInputState( hint = it[0], initialText = it[1], ) } ) } } // StateHolder 만들기
TODO Codelab: DisposableEffect step. Make MapView follow the lifecycle return remember { MapView(context).apply { id = R.id.map onCreate(Bundle()) } } } MapView의 경우 Lifecycle에 따른 관리가 필요한 객체임에도 불구, 컴포저블 내에서 주기를 알 수 없음