Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Configurations must be unique" error occurring in production #831

Open
pavel163 opened this issue Dec 27, 2024 · 8 comments
Open

"Configurations must be unique" error occurring in production #831

pavel163 opened this issue Dec 27, 2024 · 8 comments
Labels
question Further information is requested

Comments

@pavel163
Copy link

I have a root component on our screen which includes four configurations: A, B, C, where each configuration corresponds to a different screen. According to the logic, configurations B and C can only be added to the stack with A. However, I encounter production cases where the stack forms as A, B, C, B, which contradicts the designed behavior, as navigating to any screen should first require returning to a state where only A is in the stack.

Multiple root components are showing this issue

@arkivanov
Copy link
Owner

Sorry but I don't understand the issue. Can you ask a question or describe in more details?

@pavel163
Copy link
Author

I have a component similar to DefaultRootComponent, and it's organized in such a way that from one screen you can move to others (in the example these are Tabs). Over the last 90 days, there have been errors when a user navigated the following path: A (Tabs) -> B (DynamicFeatures) -> A (Tabs) -> C (CustomNavigation) -> A (Tabs) -> B (DynamicFeatures). For some reason, when these users returned to A (Tabs), the backstack was not cleared (the pop method was used for returning), and I receive the error java.lang.IllegalStateException - Configurations must be unique: [A, B, C, B].

@arkivanov
Copy link
Owner

It's hard to tell without any code or reproducer. Make sure you are using appropriate navigation functions. Duplicated configurations are not allowed. A possible issue is when the user double-clicks on a button or similar. Consider using pushNew for navigation.

@arkivanov arkivanov added the question Further information is requested label Dec 31, 2024
@leffsu
Copy link

leffsu commented Jan 23, 2025

Encountered it too on the latest version on the screen with tabs.

java.lang.IllegalStateException: Configurations must be unique: [Landing, Main(mainMenuId=HOME), Tasks, Home, Tasks]. when executing pushNew(Tasks).

The problem with pushNew is that it only check the last item. The workaround in my case is

    inline fun <C : Any> StackNavigator<C>.safePushNew(
        configuration: C,
        crossinline onComplete: (isSuccess: Boolean) -> Unit = {},
    ) {
        navigate(
            transformer = { stack ->
                stack.filter { it != configuration  } + configuration
            },
            onComplete = { newStack, oldStack -> onComplete(newStack.size > oldStack.size) },
        )
    }

What I mean:
we have tabs 1,2,3
we go to tab 1 - config is [1]
we go to tab 2 - config is [1,2]
we go to tab 3 - config is [1,2,3]

Scenario A (happy):
we click to go to tab 3 - config is [1,2,3], nothing happens

Scenario B (reproduced):
we go to tab X (any tab except for 3) - config is [1,2,3,X] now
because pushNew only check for the .last() item in the stack
crash if X is 1 or 2

@arkivanov
Copy link
Owner

Encountered it too on the latest version on the screen with tabs.

java.lang.IllegalStateException: Configurations must be unique: [Landing, Main(mainMenuId=HOME), Tasks, Home, Tasks]. when executing pushNew(Tasks).

The problem with pushNew is that it only check the last item. The workaround in my case is

    inline fun <C : Any> StackNavigator<C>.safePushNew(
        configuration: C,
        crossinline onComplete: (isSuccess: Boolean) -> Unit = {},
    ) {
        navigate(
            transformer = { stack ->
                stack.filter { it != configuration  } + configuration
            },
            onComplete = { newStack, oldStack -> onComplete(newStack.size > oldStack.size) },
        )
    }

What I mean: we have tabs 1,2,3 we go to tab 1 - config is [1] we go to tab 2 - config is [1,2] we go to tab 3 - config is [1,2,3]

Scenario A (happy): we go to tab 2 - config is [1,2] now we go to tab 1 - config is [1] now

Scenario B (reproduced): we go to tab 1 - config is [1,2,3,1] now because pushNew only check for the .last() item in the stack crash

It looks like you actually need pushToFront.

@leffsu
Copy link

leffsu commented Jan 23, 2025

Thank you kindly!

@pavel163 please check it out

@leffsu
Copy link

leffsu commented Jan 23, 2025

On the second thought pushToFront does not allow us to navigate back to the previous tab by clicking "Back" on the navigation bar. Gotta think about it

@arkivanov
Copy link
Owner

On the second thought pushToFront does not allow us to navigate back to the previous tab by clicking "Back" on the navigation bar. Gotta think about it

I think pushToFront is equivalent to the workaround you provided earlier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants