Skip to content

fix(react-stately): enable useControlledState early effect in React Native - #10550

Open
su-jin1425 wants to merge 2 commits into
adobe:mainfrom
su-jin1425:fix-use-controlled-state-window
Open

fix(react-stately): enable useControlledState early effect in React Native#10550
su-jin1425 wants to merge 2 commits into
adobe:mainfrom
su-jin1425:fix-use-controlled-state-window

Conversation

@su-jin1425

@su-jin1425 su-jin1425 commented Sep 1, 2026

Copy link
Copy Markdown

Description

Closes #10512

Summary

useControlledState uses an early synchronous effect to keep its internal value ref synchronized with controlled prop changes. The effect was previously enabled only when document was defined.

In React Native, document is not available, causing the early effect to become a no-op. As a result, parent-driven controlled value changes could be ignored.

Based on maintainer feedback, this PR introduces a specific check for React Native using navigator.product === 'ReactNative'. This ensures the synchronous effect runs in both standard DOM environments and React Native, without using window which would break Deno support.

Changes

  • Updated the environment check in useControlledState.ts to use typeof document !== 'undefined' || isReactNative.
  • Removed the previous JS-DOM regression test, as it did not properly simulate the non-DOM environment condition and was deemed unnecessary.

Why

React Native does not provide document, so the previous check incorrectly disabled the synchronization effect. Checking for isReactNative explicitly avoids regressions in other DOM-less environments like Deno.

Pull Request Checklist

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (N/A).
  • Filled out test instructions.
  • Updated documentation (N/A).
  • Looked at the Accessibility Practices for this feature (N/A).
  • I understand every change in this PR and can explain why it's there.
  • If AI-assisted, I followed our AI contribution guidance and pointed my assistant at CLAUDE.md.

@su-jin1425

Copy link
Copy Markdown
Author

In Short just changed document to window and added Regression test

expect(onChange).toHaveBeenLastCalledWith(3);
});

it('fires onChange twice when parent resets value', () => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

what does this test have to do with anything?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

its a additional regression test i thought it was useful, but now i saw the added test does not directly exercise the document vs window environment condition that causes the React Native bug, so it does not provide meaningful regression coverage for this change.

typeof document !== 'undefined'
? (React['useInsertionEffect'] ?? React.useLayoutEffect)
: () => {};
typeof window !== 'undefined' ? (React['useInsertionEffect'] ?? React.useLayoutEffect) : () => {};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

if I recall correctly, detecting SSR is best to check that document !== undefined and the check against window will fail against Deno

There's a check for React Native we could possibly add to this check

const isReactNative = typeof navigator !== 'undefined' && navigator.product === 'ReactNative';

typeof document !== 'undefined' || isReactNative

What do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

what was your opinion on this question?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The document check preserves existing SSR behavior, while the React Native check targets the actual issue. The tradeoff is relying on navigator.product, but this avoids changing behavior for runtimes like Deno. I think this is the safer, more targeted approach.

@su-jin1425
su-jin1425 force-pushed the fix-use-controlled-state-window branch from 8ee60a8 to 68e76be Compare September 2, 2026 02:00
@su-jin1425

Copy link
Copy Markdown
Author

@snowystinger I have chnages as per your request , rereview once again and tell if you need any more changes.

@su-jin1425

Copy link
Copy Markdown
Author

@snowystinger I think I still need more time to investigate so i will tell you when to review

And also tell me that am I in correct approach or not.

@su-jin1425 su-jin1425 changed the title fix(react-stately): use window for useControlledState early effect fix(react-stately): enable useControlledState early effect in React Native Sep 2, 2026
@su-jin1425
su-jin1425 force-pushed the fix-use-controlled-state-window branch from 68e76be to 2804a1c Compare September 2, 2026 09:02
@su-jin1425

Copy link
Copy Markdown
Author

@snowystinger I'm Ready for Rereview

snowystinger
snowystinger previously approved these changes Sep 2, 2026

@snowystinger snowystinger left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, I think this is ok. Wish we had a place to test against React Native for stately.

Comment on lines +16 to +18
const isReactNative = typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
const useEarlyEffect: typeof React.useLayoutEffect =
typeof document !== 'undefined'
typeof document !== 'undefined' || isReactNative

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

instead of checking for this, we can use a check for versions below React 19 since this was to avoid a warning that occurs in those older versions

@snowystinger

Copy link
Copy Markdown
Member

No need to request review every time, it's a lot of added noise. Team members will get to this when they can. Thanks.

@su-jin1425

Copy link
Copy Markdown
Author

Ok.

@su-jin1425
su-jin1425 force-pushed the fix-use-controlled-state-window branch from 98ecbdb to 97ea7a6 Compare September 5, 2026 01:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

useControlledState: parent-driven value changes are ignored when there is no DOM

4 participants