diff --git a/packages/react-devtools-shared/src/__tests__/profilerStore-test.js b/packages/react-devtools-shared/src/__tests__/profilerStore-test.js
index dca154ad170c6..7799756aed853 100644
--- a/packages/react-devtools-shared/src/__tests__/profilerStore-test.js
+++ b/packages/react-devtools-shared/src/__tests__/profilerStore-test.js
@@ -135,4 +135,36 @@ describe('ProfilerStore', () => {
});
}).toThrow('Cannot modify filter preferences while profiling');
});
+
+ it('should not throw if state contains a property hasOwnProperty ', () => {
+ let setStateCallback;
+ const ControlledInput = () => {
+ const [state, setState] = React.useState({hasOwnProperty: true});
+ setStateCallback = setState;
+ return state.hasOwnProperty;
+ };
+
+ const container = document.createElement('div');
+
+ // This element has to be in the
for the event system to work.
+ document.body.appendChild(container);
+
+ // It's important that this test uses legacy sync mode.
+ // The root API does not trigger this particular failing case.
+ ReactDOM.render(, container);
+
+ utils.act(() => store.profilerStore.startProfiling());
+ utils.act(() =>
+ setStateCallback({
+ hasOwnProperty: false,
+ }),
+ );
+ utils.act(() => store.profilerStore.stopProfiling());
+
+ // Only one commit should have been recorded (in response to the "change" event).
+ const root = store.roots[0];
+ const data = store.profilerStore.getDataForRoot(root);
+ expect(data.commitData).toHaveLength(1);
+ expect(data.operations).toHaveLength(1);
+ });
});
diff --git a/packages/react-devtools-shared/src/backend/renderer.js b/packages/react-devtools-shared/src/backend/renderer.js
index 0ef641e410216..fbbac1bcdc6d7 100644
--- a/packages/react-devtools-shared/src/backend/renderer.js
+++ b/packages/react-devtools-shared/src/backend/renderer.js
@@ -1221,15 +1221,18 @@ export function attach(
}
function isEffect(memoizedState) {
+ if (memoizedState === null || typeof memoizedState !== 'object') {
+ return false;
+ }
+ const {deps} = memoizedState;
+ const hasOwnProperty = Object.prototype.hasOwnProperty.bind(memoizedState);
return (
- memoizedState !== null &&
- typeof memoizedState === 'object' &&
- memoizedState.hasOwnProperty('tag') &&
- memoizedState.hasOwnProperty('create') &&
- memoizedState.hasOwnProperty('destroy') &&
- memoizedState.hasOwnProperty('deps') &&
- (memoizedState.deps === null || isArray(memoizedState.deps)) &&
- memoizedState.hasOwnProperty('next')
+ hasOwnProperty('create') &&
+ hasOwnProperty('destroy') &&
+ hasOwnProperty('deps') &&
+ hasOwnProperty('next') &&
+ hasOwnProperty('tag') &&
+ (deps === null || isArray(deps))
);
}