Skip to content

Commit

Permalink
Reset controlled .checked upon hydration
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiebits committed Sep 28, 2023
1 parent 76e127b commit e4e3432
Show file tree
Hide file tree
Showing 2 changed files with 6 additions and 7 deletions.
2 changes: 1 addition & 1 deletion packages/react-dom-bindings/src/client/ReactDOMInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ export function initInput(
typeof checkedOrDefault !== 'symbol' &&
!!checkedOrDefault;

if (isHydrating) {
if (isHydrating && checked == null) {
// Detach .checked from .defaultChecked but leave user input alone
node.checked = node.checked;
} else {
Expand Down
11 changes: 5 additions & 6 deletions packages/react-dom/src/__tests__/ReactDOMInput-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1300,23 +1300,22 @@ describe('ReactDOMInput', () => {

// Currently, we don't fire onChange when hydrating
assertLog([]);
// Strangely, we leave `b` checked even though we rendered A with
// checked={true} and B with checked={false}. Arguably this is a bug.
expect(a.checked).toBe(false);
expect(b.checked).toBe(true);
// Instead, we reset the form to match React state and you lose the user's input.
expect(a.checked).toBe(true);
expect(b.checked).toBe(false);
expect(c.checked).toBe(false);
expect(isCheckedDirty(a)).toBe(true);
expect(isCheckedDirty(b)).toBe(true);
expect(isCheckedDirty(c)).toBe(true);
assertInputTrackingIsCurrent(container);

// If we click on C now though...
// If we click on C now...
await act(async () => {
setUntrackedChecked.call(c, true);
dispatchEventOnNode(c, 'click');
});

// then since C's onClick doesn't set state, A becomes rechecked.
// there's no change.
assertLog(['click c']);
expect(a.checked).toBe(true);
expect(b.checked).toBe(false);
Expand Down

0 comments on commit e4e3432

Please sign in to comment.