현재 상황


현재 Compose내에서 사용하는 State값은 2개

  • TextField에 사용 된 String 타입의 query

    val (query, setQuery) = rememberSaveable { mutableStateOf("") }
  • LazyVerticalGrid에 결과 값으로 보일 list

    val list = mutableStateListOf<Document>()

  • Textfield에 onValueChange() 이벤트 호출 마다
    query state값을 변경해주는 setQuery()가 호출되기 때문에 리컴포지션 된다

  • 상태값을 저장(remember)하는 건 query

  • 리컴포지션이 일어나면 query는 유지되고 list를 초기화 된다


그래서 내가 하고 싶은거?

리컴포지션이 일어나도 list값이 유지 되는 것

저번 블로그 글에도 남겼지만
리스트 상태값에 대한 이해도가 없어서

remember{}로 감싸면? 앱이 실행안되고 강제 종료행이였다..


자 그러면 공부 시작!

[compose] update list state


compose 상태관리 공식문서를 들어가 "list"로 키워드를 검색 해보자

빨간색 박스로 뭐라 무섭게 나와있다..


어색한 말을 좀 가공해서 쉽게 읽어보면..

ArrayList or mutableListof()와 같은 변경 가능한 List 객체를
state로 사용하면 잘못된 데이터 또는 오래된 데이터가 표시 될 수 있다.

..?..

변경 가능한 객체, ArrayList와 같은 객체는 Compose에서 관찰 할 수 없고
객체가 변경 될 때 리컴포지션을 트리거하지 않습니다

..!..??????

그러니 변경은 가능하지만 관찰(observe)은 안되는 객체를 사용하지 마시고,
State<List<T>>listOf() 같은 변경 불가능한 객체에
관찰 가능한 데이터 홀더를 사용하는 것이 좋습니다.

아 그러니깐 결론적으론 위에서 사용했던
mutableStateListOf<Document>()로 사용하면
state 변경 감지도 못하고, 변경되도 리컴포지션을 트리거 하지 않는다..?

그러니깐 변경 불가능한 List객체 state로 사용하고 관찰해보자?

바로 공식문서 대로 코드를 바로 바꿔보자 !!!


기존 코드

setContent {
		val (query, setQuery) = rememberSaveable { mutableStateOf("") }
		val list = mutableStateListOf<Document>()

		...

		SearchPage(
        query = query, setQuery = setQuery,
        onSearch = {
            searchImage(query = query) {
                val docs = it["documents"] as List<*>
                for (doc in docs) {
                    val castedDoc =
                        Document.fromJson(json = doc as Map<String, Any>)
                    list.add(castedDoc)
                }
            }
        },
        results = list,
    )

		...
}
  • SearchPage는 제가 만든 컴포저블 함수입니다.

  • Document는 제가 만든 모델 객체 입니다
    (remote API 호출 후 응답 값과 동일한 데이터 모델)

그러니 List<T> 제네릭 타입은 용도에 맞춰 바꿔서 사용하세요!


수정 후 코드

setContent {
		val (query, setQuery) = rememberSaveable { mutableStateOf("") }
		val list = rememberSaveable { mutableStateOf(listOf<Document>()) }

		...

		SearchPage(
        query = query, setQuery = setQuery,
        onSearch = {
            searchImage(query = query) {
                val results = it["documents"] as List<*>
                val docs = mutableListOf<Document>();
                for (result in results) {
                    val castedDoc =
                        Document.fromJson(json = result as Map<String, Any>)
                    docs.add(castedDoc)
                }
                list.value = docs
            }
        },
        results = list.value,
    )

		...
}
  • listOf<T>: list를 변경 불가능한 상태로 변경해줍니다

  • 기존 String, Int 사용했던것과 마찬가지로 변경가능한 state 값으로 다루기 위해 mutableStateOf{} 감싸줍니다

  • 그러고 화면을 전환해도 데이터가 유지 되도록 rememberSaveable{}로 감싸줍니다


결과

영상엔 담기지 않았지만
list 상태값 변경되었을 때 리컴포저블 하는 것

화면에 보이는 대로 query가 변경되어 리컴포저블 되더라도
값을 remeber 되어 기존 값을 유지하는 것


성공적이다... !

당연히 list값을 상태 값으로 바라볼 수 있게끔 mutableStateListOf가 있어서 기존에 사용했던 mutableStateOf를 못 쓰는 줄 알았는데 변경 불가능한 List 객체였다면 쓸 수 있었다.

변경가능한 객체를 상태로 바라볼 때 리컴포저블 트리거도 안되고 이슈가 많다면 왜 따로 함수까지 만들어줬을까? 의문이긴하지만

공식문서에 적힌대로 변경 불가능한 객체를 state를 observing(관찰)한다!는 핵심을 얻어가며 오늘 글을 마무리 지어본다.

profile
𝙸 𝚊𝚖 𝚊 𝚌𝚞𝚛𝚒𝚘𝚞𝚜 𝚍𝚎𝚟𝚎𝚕𝚘𝚙𝚎𝚛 𝚠𝚑𝚘 𝚎𝚗𝚓𝚘𝚢𝚜 𝚍𝚎𝚏𝚒𝚗𝚒𝚗𝚐 𝚊 𝚙𝚛𝚘𝚋𝚕𝚎𝚖. 🇰🇷👩🏻‍💻

0개의 댓글