Skip to content

Fix radios component with defaultChecked inputs and conditional content#377

Merged
colinrotherham merged 15 commits into
mainfrom
conditional-default-checked
May 15, 2026
Merged

Fix radios component with defaultChecked inputs and conditional content#377
colinrotherham merged 15 commits into
mainfrom
conditional-default-checked

Conversation

@colinrotherham
Copy link
Copy Markdown
Collaborator

@colinrotherham colinrotherham commented May 14, 2026

This PR fixes #374

In v6.0.0 we accidentally left internal useState() in the following components (from v5.x)

  1. Radios selectedRadio and setSelectedRadio()
  2. Radios item selectedRadio and setSelected()

These should have been removed for the v6 release where:

  • All form components support ref for DOM element access
  • All form components support defaultValue, value and onChange handlers

This makes sure radio buttons work with either checked (controlled) and defaultChecked (uncontrolled)

What was the fix?

  • Removing internal state updates prevents unnecessary re-renders
  • Falling back to name (not just id) before generating IDs prevents unnnecessary re-renders

What was the bug?

Radio inputs previously decided whether to show conditional content using:

const shouldShowConditional = selectedRadio === radioReference && checked !== false;

Notice that defaultChecked is not included? Only controlled inputs worked

To workaround this, the previous version tried to sync up the internal context/state with:

useEffect(() => {
  if (defaultChecked) setSelected(radioReference);
}, [defaultChecked, setSelected, radioReference]);

useEffect(() => {
  if (checked) setSelected(radioReference);
}, [checked, setSelected, radioReference]);

But that caused problems with modern React and NHS.UK frontend conditional content synchronisation

  • The 1st render with defaultChecked showed the conditional content
  • The 2nd render after the useEffect() hide the conditional content (unless already checked)
  • The 3rd render after re-clicking the input finally shows the conditional content

@colinrotherham colinrotherham force-pushed the development-guidance branch from 5f73033 to 12a9c50 Compare May 14, 2026 11:53
@colinrotherham colinrotherham force-pushed the conditional-default-checked branch from 165b241 to fe91590 Compare May 14, 2026 11:54
@mikemonteith
Copy link
Copy Markdown

I've tested this locally in my service (https://github.com/NHSDigital/record-a-vaccination-service) and it's working perfectly.

@mikemonteith
Copy link
Copy Markdown

57 changed files and 17 commits in this PR. I assume some of these commits are not indended for this PR?

@colinrotherham colinrotherham force-pushed the development-guidance branch from 12a9c50 to ab2b166 Compare May 14, 2026 14:03
@colinrotherham colinrotherham force-pushed the conditional-default-checked branch from fe91590 to 381cb0c Compare May 14, 2026 14:04
@colinrotherham colinrotherham changed the base branch from development-guidance to align-stories May 14, 2026 14:04
@colinrotherham
Copy link
Copy Markdown
Collaborator Author

57 changed files and 17 commits in this PR. I assume some of these commits are not indended for this PR?

Well spotted thanks, I've changed the PR base to #375

Copy link
Copy Markdown

@mikemonteith mikemonteith left a comment

Choose a reason for hiding this comment

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

LGTM

@colinrotherham colinrotherham force-pushed the align-stories branch 2 times, most recently from a0eac0d to 2e8a116 Compare May 14, 2026 16:18
@colinrotherham colinrotherham force-pushed the conditional-default-checked branch 3 times, most recently from 93717ca to d36cdb0 Compare May 14, 2026 21:36
@colinrotherham colinrotherham marked this pull request as ready for review May 15, 2026 10:08
Base automatically changed from align-stories to main May 15, 2026 11:55
@colinrotherham colinrotherham force-pushed the conditional-default-checked branch 2 times, most recently from 0159b0c to 71d1959 Compare May 15, 2026 12:22
@colinrotherham colinrotherham changed the title Fix radios component with defaultChecked inputs Fix radios component with defaultChecked inputs and conditional content May 15, 2026
@colinrotherham colinrotherham force-pushed the conditional-default-checked branch from c3310bf to 63a2d6f Compare May 15, 2026 14:20
@colinrotherham colinrotherham force-pushed the conditional-default-checked branch from 63a2d6f to 2ab21ed Compare May 15, 2026 14:52
@frankieroberto
Copy link
Copy Markdown
Contributor

@colinrotherham thank you! This unblocks an issue found by @andyrae6 in https://github.com/NHSDigital/record-a-vaccination-service/pull/55

Comment on lines +58 to +61
// Use `name` prop when `id` is missing, otherwise server-side rendered (SSR)
// components will use different generated IDs after client-side hydration
// https://react.dev/link/hydration-mismatch
const elementID = name && !id ? `${name}-${inputType}` : (id ?? generatedID);
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

When tesing with Next.js another re-rendering issue came up. From the changelog:

Note: Form components with missing id (or idPrefix) now fall back to the name prop before generating IDs. This helps prevent hydration mismatch errors with server-side rendering (SSR) HTML.

@colinrotherham colinrotherham force-pushed the conditional-default-checked branch from 2ab21ed to b5a4c31 Compare May 15, 2026 15:00
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
26.8% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@colinrotherham
Copy link
Copy Markdown
Collaborator Author

Thanks @mikemonteith and @frankieroberto

The Sonar duplications are largely from the Storybook examples so we should ignore them in future

@colinrotherham colinrotherham added this pull request to the merge queue May 15, 2026
Merged via the queue into main with commit 0e0f3d4 May 15, 2026
2 of 3 checks passed
@colinrotherham colinrotherham deleted the conditional-default-checked branch May 15, 2026 15:08
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.

Radios with conditional reveal and defaultChecked issues

3 participants