@samitouri / QOS-React / commits / f5f2799a8d

DevTools: Fix inspecting components with multiple reads of the same Context in React 17 (#28974)

Sebastian Silbermann committed May 2, 2024 at 22:08 UTC f5f2799a8d0b37487c9674159846cbb7696febd3
2 files changed +52 -8
packages/react-debug-tools/src/ReactDebugHooks.js
+13 -8
@@ -177,15 +177,20 @@ function readContext<T>(context: ReactContext<T>): T {
177 );
178 }
179
180 + let value: T;
181 // For now we don't expose readContext usage in the hooks debugging info.
181 - const value = hasOwnProperty.call(currentContextDependency, 'memoizedValue')
182 - ? // $FlowFixMe[incompatible-use] Flow thinks `hasOwnProperty` mutates `currentContextDependency`
183 - ((currentContextDependency.memoizedValue: any): T)
184 - : // Before React 18, we did not have `memoizedValue` so we rely on `setupContexts` in those versions.
185 - // $FlowFixMe[incompatible-use] Flow thinks `hasOwnProperty` mutates `currentContextDependency`
186 - ((currentContextDependency.context._currentValue: any): T);
187 - // $FlowFixMe[incompatible-use] Flow thinks `hasOwnProperty` mutates `currentContextDependency`
188 - currentContextDependency = currentContextDependency.next;
182 + if (hasOwnProperty.call(currentContextDependency, 'memoizedValue')) {
183 + // $FlowFixMe[incompatible-use] Flow thinks `hasOwnProperty` mutates `currentContextDependency`
184 + value = ((currentContextDependency.memoizedValue: any): T);
185 +
186 + // $FlowFixMe[incompatible-use] Flow thinks `hasOwnProperty` mutates `currentContextDependency`
187 + currentContextDependency = currentContextDependency.next;
188 + } else {
189 + // Before React 18, we did not have `memoizedValue` so we rely on `setupContexts` in those versions.
190 + // Multiple reads of the same context were also only tracked as a single dependency.
191 + // We just give up on advancing context dependencies and solely rely on `setupContexts`.
192 + value = context._currentValue;
193 + }
194
195 return value;
196 }
packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js
+39
@@ -833,6 +833,45 @@ describe('ReactHooksInspectionIntegration', () => {
833 `);
834 });
835
836 + // @reactVersion >= 16.8
837 + it('should inspect the value of the current provider in useContext reading the same context multiple times', async () => {
838 + const ContextA = React.createContext('default A');
839 + const ContextB = React.createContext('default B');
840 + function Foo(props) {
841 + React.useContext(ContextA);
842 + React.useContext(ContextA);
843 + React.useContext(ContextB);
844 + React.useContext(ContextB);
845 + React.useContext(ContextA);
846 + React.useContext(ContextB);
847 + React.useContext(ContextB);
848 + React.useContext(ContextB);
849 + return null;
850 + }
851 + let renderer;
852 + await act(() => {
853 + renderer = ReactTestRenderer.create(
854 + <ContextA.Provider value="contextual A">
855 + <Foo prop="prop" />
856 + </ContextA.Provider>,
857 + {unstable_isConcurrent: true},
858 + );
859 + });
860 + const childFiber = renderer.root.findByType(Foo)._currentFiber();
861 + const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
862 +
863 + expect(normalizeSourceLoc(tree)).toEqual([
864 + expect.objectContaining({value: 'contextual A'}),
865 + expect.objectContaining({value: 'contextual A'}),
866 + expect.objectContaining({value: 'default B'}),
867 + expect.objectContaining({value: 'default B'}),
868 + expect.objectContaining({value: 'contextual A'}),
869 + expect.objectContaining({value: 'default B'}),
870 + expect.objectContaining({value: 'default B'}),
871 + expect.objectContaining({value: 'default B'}),
872 + ]);
873 + });
874 +
875 it('should inspect forwardRef', async () => {
876 const obj = function () {};
877 const Foo = React.forwardRef(function (props, ref) {