noize
noize

Reputation: 1

How do I fetch and prepare data before displaying a composable?

In Jetpack Compose, what would be a proper way to fetch all required data before a composable used to edit this data is created?

What I have are two composables, one is supposed to fetch all required data that is to be displayed, the child is supposed to allow the user to edit this data via form elements. The data is fetched in the view model and locally stored in the child composable as a rememberSaveable() during editing:

ViewModel contains:

private val _userFetchedForEditing = MutableStateFlow<User?>(null)
val userFetchedForEditing: StateFlow<User?> = _userFetchedForEditing.asStateFlow()

fun fetchUserForEditingByName(name: String) {
    _userFetchedForEditing.value = null
    viewModelScope.launch {
        _userFetchedForEditing.value = userRepository.getUserByName(name).firstOrNull()
    }
}

Compose code parent:

composable(
    route = "${Screen.UserEditor.route}?userName={userName}",
    arguments = listOf(navArgument("userName") {
        type = NavType.StringType
        nullable = false
    }),
) { navBackStackEntry ->
    val userName: String? = navBackStackEntry.arguments?.getString("userName")

    LaunchedEffect(userName) {
        userName?.let { viewModel.fetchUserForEditingByName(it) }
    }

    val userFetchedForEditing by viewModel.userFetchedForEditing.collectAsState()
    if (userFetchedForEditing == null) {
        LoadingScreen()
    } else {
        UserEditor(viewModel, navController, modifier, userFetchedForEditing)
    }
}

Compose code child:

@Composable
private fun UserEditor(
    viewModel: MainViewModel,
    navController: NavHostController,
    modifier: Modifier = Modifier,
    givenInput: User? = null,
) {
    var editableInputUser by rememberSaveable(saver = userSaver) {
        mutableStateOf(givenInput ?: User())
    }

    //this causes a visible screen refresh of the shown user after a couple milliseconds, but is needed or the editableInputUser is not updated to the givenInput sometimes
    LaunchedEffect(givenInput) {
        editableInputUser = givenInput ?: User()
    }

    UserEditorForm(
        //takes editableInputUser and displays forms for updating values
    )
}

My concrete question is: What would be the way to go here, to allow displaying and editing the user using Jetpack Compose?

It currently feels like I have to abuse the LaunchedEffect to yank the displayed data into what should be displayed.

Upvotes: 0

Views: 121

Answers (1)

tyg
tyg

Reputation: 15763

You already have most of what you need: You display the LoadingScreen until the data is available, and then call UserEditor when it is loaded.

Some minor things seem to be a little bit off, though. Why is the UserEditor's givenInput parameter nullable? Make it non-nullable and then call it like this:

userFetchedForEditing.also {
    if (it == null) {
        LoadingScreen()
    } else {
        UserEditor(viewModel, navController, modifier, it)
    }
}

Also, I don't see why the LaunchedEffect is necessary. Do you expect the givenInput to change by some other means while the user is editing it? And do you really want to overwrite everything the user entered so far when that happens?

And finally, it seems easier to store the editableInputUser in the view model (as a MutableStateFlow) instead of jumping through hoops with rememberSaveable and a custom saver.

Upvotes: 0

Related Questions