#C’est quoi l’état

  • Compose est un framework UI qui se base sur l’état des donnĂ©es pour dĂ©ssiner l’interface (UI) correspondante.
  • L’état est un ensemble de donnĂ©es qui peuvent ĂȘtre modifiĂ©es par l’utilisateur ou par le systĂšme.
  • L’état est immutable, et les interfaces sont aussi immutable.
    • ☝Cela signifie que chaque fois que l’état change, une nouvelle interface est gĂ©nĂ©rĂ©e.

Quand on écrit un composable, on utilise une syntaxe indempodente, via une fonction. Pourquoi une fonction = un composable ?

  • Les fonctions sont indĂ©pendantes de l’état
  • Une fonction est indempodente, pour une entrĂ©e donnĂ©e, elle retourne toujours la mĂȘme sortie, ce qui correspond Ă  notre idĂ©e de l’immuabilitĂ©.

#Un exemple simple

kotlin
@Composable
fun MyComposable() {
  var counter = 1

  MyCountableButton(count = counter) {
    counter++
  }
}

@Composable
fun MyCountableButton(count: Int, onClick: () -> Unit) {
    Button(onClick = onClick) {
        Text("Click me ${count}")
    }
}
  • Dans cet exemple, le composable MyComposable contient un Ă©tat counter qui est incrĂ©mentĂ© Ă  chaque clic sur le bouton.
  • Sauf que ce code ne fonctionne pas, car counter n’est pas un Ă©tat, mais une variable locale. En effet, chaque fois que MyComposable est appelĂ©, counter est rĂ©initialisĂ© Ă  1, puisqu’une fonction est indempodente, elle produira toujours le mĂȘme rĂ©sultat.

#On est donc bloquĂ© ? đŸ€”

Oui, et non.

Compose met à disposition des moyens de gérer les transitions entre les états, et de les rendre persistants.

kotlin
@Composable
fun MyComposable() {
  // Remember va permettre de conserver l'Ă©tat entre les appels, d'oĂč le nom
  val counter = remember { mutableStateOf(1) }

  MyCountableButton(count = counter.value) {
    counter.value++
  }
}

@Composable
fun MyCountableButton(count: Int, onClick: () -> Unit) {
    Button(onClick = onClick) {
        Text("Click me ${count}")
    }
}

#Remember et MutableState

↔ Pour ceux qui aurait dĂ©jĂ  fait du React les remember sont l’équivalent des useMemo en React.

  • remember permet de conserver l’état entre les appels
  • mutableStateOf permet de dĂ©clarer un Ă©tat mutable, il va permettre de notifier Compose que l’état a changĂ©, et de redessiner l’interface.

Il existe plusieurs rememberable :

  • remember : pour conserver un Ă©tat entre les modifications des variables
  • rememberSaveable : pour conserver un Ă©tat entre les appels, mais aussi entre les changements de configuration (rotation de l’écran)
  • rememberCoroutineScope : pour conserver un scope de coroutine -> Important Ă  retenir
  • rememberSnackbarHostState : pour conserver un Ă©tat de snackbar
  • ... : il en existe d’autres

đŸ§© Remember est utile pour des opĂ©rations de modification d’une variable directes.

#Side Effect

↔ Pour ceux qui aurait dĂ©jĂ  fait du React les Launched Effect sont l’équivalent des useEffect en React.

Pour effectuer des opérations asynchrones ou plus complexe que des opérations directes, on utilise les SideEffect.

#Exemple simple

kotlin
@Composable
fun MyComposable() {
  val counter = remember { mutableStateOf(1) }

  MyCountableButton(count = counter.value) {
    counter.value++
  }

  LaunchedEffect(counter.value) {
    delay(1000) // Simulation d'une opération asynchrone ou complexe
    counter.value++
  }
}
  • Si vous deviez faire un appel rĂ©seau, ou une opĂ©ration complexe, vous pouvez utiliser les LaunchedEffect de concert avec rememberCoroutineScope.

#Exemple appel réseau

kotlin

fun refreshData() {
    // Appel réseau
}

@Composable
fun MyApp() {
    val scope = rememberCoroutineScope()
    var orderList by remember { mutableStateOf(emptyList<Order>()) }

    // shouldRefresh est une fonction qui permet au composable de demander un rafraichissement des données à son parent
    OrderScreen(orderList = orderList, shouldRefresh = {
        scope.launch {
            orderList = refreshData() ?: emptyList()
        }
    })

    // On lance le rafraichissement des donnĂ©es au dĂ©marrage d'oĂč le 'true'
    LaunchedEffect(true) {
        scope.launch {
            orderList = refreshData() ?: emptyList()
        }
    }
}

#ViewModel

Les viewModel sont des classes qui permettent de gĂ©rer l’état de l’application, et de le partager entre les composants. C’est le niveau ultime de partage de l’état.

#Tableau de comparaison des solutions et leur utilité

SolutionUtilité
Variable localePour des opĂ©rations simples, sans partage de l’état
RememberPour conserver l’état entre les appels, idĂ©al pour une hierarchie de composants simples
SideEffectPour des opérations asynchrones ou complexes
ViewModelPour partager l’état entre les composants, idĂ©al pour une application complexe

#Aller plus loin

Tout est expliqué ici, dans un article trÚs bien fait par Google : State in Jetpack Compose

#✅ Critùres de Validation

CritĂšreValidation
Vous savez définir State[ ]
Vous avez compris le fonctionnement de Remember[ ]
 Vous savez comment et quand utiliser SideEffect[ ]
Vous savez comment et quand utiliser ViewModel[ ]
 Vous savez définir State Hoisting[ ]