Skip to content

Version 10.0.0 beta04#2384

Draft
demolaf wants to merge 1 commit into
masterfrom
version-10.0.0-beta04
Draft

Version 10.0.0 beta04#2384
demolaf wants to merge 1 commit into
masterfrom
version-10.0.0-beta04

Conversation

@demolaf

@demolaf demolaf commented Jul 10, 2026

Copy link
Copy Markdown
Member

No description provided.

…2383)

* fix(auth): validate display name only when required in sign-up form

* fix(auth): use locale-safe matcher for sign-up button in test

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the form validation logic in SignUpUI.kt to only validate the display name if it is required by the provider, and adds a corresponding unit test in SignUpUITest.kt to verify this behavior. Feedback was provided regarding the use of derivedStateOf for isFormValid, which is an anti-pattern here because the inputs are plain strings rather than Compose State objects, leading to unnecessary allocations. Additionally, calling validate() inside the block causes side-effects during composition. It is recommended to simplify isFormValid to a plain Boolean using remember and update the button's enabled state accordingly.

Comment on lines 95 to +98
val isFormValid = remember(displayName, email, password, confirmPassword) {
derivedStateOf {
listOf(
displayNameValidator.validate(displayName),
!provider.isDisplayNameRequired || displayNameValidator.validate(displayName),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Unnecessary derivedStateOf and Side-Effects during Composition

There are two main issues with the current implementation of isFormValid:

  1. derivedStateOf Anti-Pattern:
    derivedStateOf is designed to derive state from other Compose State objects (like MutableState). Here, the inputs (displayName, email, password, confirmPassword) are plain String parameters, not Compose States. Because they are plain values, Compose cannot track them inside the derivedStateOf block. To make it work, they are passed as keys to remember. However, when any of these keys change, the entire remember block is re-executed, creating a brand new derivedStateOf instance on every keystroke. This completely defeats the purpose of derivedStateOf and adds unnecessary allocation and registration overhead.

  2. Side-Effects during Composition:
    Inside the derivedStateOf block, validate() is called on the validators (e.g., displayNameValidator.validate(displayName)). The validate() function is not pure; it mutates the internal state (_validationStatus) of the validator. Running side-effects inside a composition/calculation block is highly discouraged in Compose as it can lead to inconsistent UI states or unexpected behavior.

Recommended Solution

Simplify isFormValid to a plain Boolean using remember with keys, and update its usage in the Button to remove .value:

// 1. Simplify isFormValid to a plain Boolean
val isFormValid = remember(displayName, email, password, confirmPassword) {
    listOf(
        !provider.isDisplayNameRequired || displayNameValidator.validate(displayName),
        emailValidator.validate(email),
        passwordValidator.validate(password),
        confirmPasswordValidator.validate(confirmPassword)
    ).all { it }
}

// 2. Update the Button's enabled state (around line 203) to use isFormValid directly:
Button(
    onClick = { onSignUpClick() },
    enabled = !isLoading && isFormValid, // Removed .value
) {
    // ...
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant