Skip to content

Commit 41da88f

Browse files
[internal] Improve useControlled() strict mode handling (#46807)
Signed-off-by: Olivier Tassinari <oliviertassinari@users.noreply.github.com>
1 parent 3634b32 commit 41da88f

File tree

2 files changed

+48
-33
lines changed

2 files changed

+48
-33
lines changed

packages/mui-utils/src/useControlled/useControlled.test.tsx

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ type TestComponentProps<T> = TestProps<T> & {
1717

1818
type SetProps = <T>(props: TestProps<T>) => void;
1919

20-
const TestComponent = <T,>({ value: valueProp, defaultValue, children }: TestComponentProps<T>) => {
20+
function TestComponent<T>({ value: valueProp, defaultValue, children }: TestComponentProps<T>) {
2121
const [value, setValue] = useControlled({
2222
controlled: valueProp,
2323
default: defaultValue,
2424
name: 'TestComponent',
2525
});
2626
return children({ value, setValue });
27-
};
27+
}
2828

2929
describe('useControlled', () => {
3030
const { render } = createRenderer();
@@ -90,39 +90,56 @@ describe('useControlled', () => {
9090
);
9191
});
9292

93-
it('warns when changing the defaultValue prop after initial rendering', () => {
94-
let setProps: SetProps;
93+
describe('warns when changing the defaultValue prop after initial rendering', () => {
94+
it('should detect changes', () => {
95+
let setProps: SetProps;
9596

96-
expect(() => {
97-
({ setProps } = render(<TestComponent>{() => null}</TestComponent>));
98-
}).not.toErrorDev();
97+
expect(() => {
98+
({ setProps } = render(<TestComponent>{() => null}</TestComponent>));
99+
}).not.toErrorDev();
99100

100-
expect(() => {
101-
setProps({ defaultValue: 1 });
102-
}).toErrorDev(
103-
'MUI: A component is changing the default value state of an uncontrolled TestComponent after being initialized.',
104-
);
105-
});
101+
expect(() => {
102+
setProps({ defaultValue: 1 });
103+
}).toErrorDev(
104+
'MUI: A component is changing the default value state of an uncontrolled TestComponent after being initialized.',
105+
);
106+
});
106107

107-
it('should not raise a warning if changing the defaultValue when controlled', () => {
108-
let setProps: SetProps;
108+
it('should not warn when controlled', () => {
109+
let setProps: SetProps;
109110

110-
expect(() => {
111-
({ setProps } = render(
112-
<TestComponent value={1} defaultValue={0}>
113-
{() => null}
114-
</TestComponent>,
115-
));
116-
}).not.toErrorDev();
111+
expect(() => {
112+
({ setProps } = render(
113+
<TestComponent value={1} defaultValue={0}>
114+
{() => null}
115+
</TestComponent>,
116+
));
117+
}).not.toErrorDev();
117118

118-
expect(() => {
119-
setProps({ defaultValue: 1 });
120-
}).not.toErrorDev();
121-
});
119+
expect(() => {
120+
setProps({ defaultValue: 1 });
121+
}).not.toErrorDev();
122+
});
122123

123-
it('should not raise a warning if setting NaN as the defaultValue when uncontrolled', () => {
124-
expect(() => {
125-
render(<TestComponent defaultValue={NaN}>{() => null}</TestComponent>);
126-
}).not.toErrorDev();
124+
it('should not warn when NaN', () => {
125+
expect(() => {
126+
render(<TestComponent defaultValue={NaN}>{() => null}</TestComponent>);
127+
}).not.toErrorDev();
128+
});
129+
130+
it('should not warn when an array', () => {
131+
function TestComponentArray() {
132+
useControlled({
133+
controlled: undefined,
134+
default: [],
135+
name: 'TestComponent',
136+
});
137+
return null;
138+
}
139+
140+
expect(() => {
141+
render(<TestComponentArray />);
142+
}).not.toErrorDev();
143+
});
127144
});
128145
});

packages/mui-utils/src/useControlled/useControlled.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,7 @@ export default function useControlled<T = unknown>(
5252
const { current: defaultValue } = React.useRef(defaultProp);
5353

5454
React.useEffect(() => {
55-
// Object.is() is not equivalent to the === operator.
56-
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is for more details.
57-
if (!isControlled && !Object.is(defaultValue, defaultProp)) {
55+
if (!isControlled && JSON.stringify(defaultProp) !== JSON.stringify(defaultValue)) {
5856
console.error(
5957
[
6058
`MUI: A component is changing the default ${state} state of an uncontrolled ${name} after being initialized. ` +

0 commit comments

Comments
 (0)