airbnb / mavericks

Mavericks: Android on Autopilot
https://airbnb.io/mavericks/
Apache License 2.0
5.83k stars 500 forks source link

How to reset Async request's state? #121

Closed qbait closed 5 years ago

qbait commented 5 years ago

I do a request, it gives me a failure, I show to user alert dialog informing about the failure and I wanna reset the state of my request to Uninitialized because if I won't, the alert will be shown every time the state changes. Below is my attempt to solve this problem, however it doesn't set the request correctly. What's the best practice to do that?

    override fun epoxyController() = simpleController(viewModel) { state ->
        when (state.request) {
            is Success -> {}
            is Loading -> loadingRow {
                id("loading")
            }
            is Fail -> {
                val message = state.request.error.prettyMessage()
                alert(message, "Failed") {
                    okButton {
                        state.copy(request = Uninitialized) // Here is the problem. How to reset request?
                    }
                }.show()
            }
            else -> button {
                id("sendButton")
                title("Send Link")
            }
        }
    }
gpeal commented 5 years ago

@qbait We recommend using selectSubscribe for showing something only a single time when an event changes.

94 and #113 for more context.

qbait commented 5 years ago

Thanks @gpeal selectSubscribe works like a charm in onViewCreated, but inside epoxyController it behaves randomly. I solved it by checking my state in 2 places

1) in onViewCreated for displaying dialogs

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewModel.selectSubscribe(ResetState::request) {
            when (it) {
                is Success -> {
                    alert("Check your inbox") {
                        okButton {
                            loginNav.popBackStack()
                        }
                    }.show()
                }
                is Fail -> {
                    alert("Check your internet connection.", "Something went wrong") {
                        okButton {}
                    }.show()
                }
            }
        }
    }

2) In my EpoxyController for displaying rows

    override fun epoxyController() = simpleController(viewModel) { state ->
        ...
        when (state.request) {
            is Loading -> loadingRow {
                id("loading")
            }
            else -> button {
                id("sendButton")
                title("Send Link")
                onClick { _ -> viewModel.reset() }
                isEnabled(state.resetForm.email.isValid)
            }
        }
    }

Is it currently the best approach or I'm missing something here?

[EDIT] I've just noticed that when using selectSubscribe, my dialog is still shown after the app is coming from the background.

gpeal commented 5 years ago

@qbait You'll want the behavior post this PR (https://github.com/airbnb/MvRx/pull/113) with isUnique set to true.

gpeal commented 5 years ago

And the subscription should be in onCreate (https://github.com/airbnb/MvRx/issues/118)

qbait commented 5 years ago

Kudos to @BenSchwab. Looking forward to seeing it in the master then. Thank you.

ubuntudroid commented 5 years ago

@gpeal @qbait I was running into the exact same issue and implemented everything according to the solutions provided here (selectSubscribe with uniqueOnly=true in onCreate()). I still see the dialog (in my case it is a Toast) popping up after orientation changes.

After some tinkering I just ended up ditching uniqueOnly and selectSubscribe because they didn't seem to have any effect on this issue and just reset the Async to Uninitialized when showing the toast (similar to what has been described here: https://github.com/airbnb/MvRx/issues/137#issuecomment-450288720).

I don't particularly like that solution, but it seems to work fine in all cases I tested.