diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 5da57d87b3ff..1389ab5bcc2f 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -233,6 +233,7 @@ const Dispatcher: DispatcherType = { type HooksNode = { index: number, + isEditable: boolean, name: string, value: mixed, subHooks: Array, @@ -406,6 +407,7 @@ function buildTree(rootStack, readHookLog): HooksTree { let children = []; levelChildren.push({ index: -1, + isEditable: false, name: parseCustomHookName(stack[j - 1].functionName), value: undefined, subHooks: children, @@ -415,9 +417,11 @@ function buildTree(rootStack, readHookLog): HooksTree { } prevStack = stack; } + const {primitive} = hook; levelChildren.push({ - index: hook.primitive === 'DebugValue' ? -1 : index++, - name: hook.primitive, + index: primitive === 'DebugValue' ? -1 : index++, + isEditable: primitive === 'Reducer' || primitive === 'State', + name: primitive, value: hook.value, subHooks: [], }); diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js index 870bd19f3f6c..8121e5e4fb46 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js @@ -28,6 +28,7 @@ describe('ReactHooksInspection', () => { let tree = ReactDebugTools.inspectHooks(Foo, {}); expect(tree).toEqual([ { + isEditable: true, index: 0, name: 'State', value: 'hello world', @@ -49,11 +50,13 @@ describe('ReactHooksInspection', () => { let tree = ReactDebugTools.inspectHooks(Foo, {}); expect(tree).toEqual([ { + isEditable: false, index: -1, name: 'Custom', value: __DEV__ ? 'custom hook label' : undefined, subHooks: [ { + isEditable: true, index: 0, name: 'State', value: 'hello world', @@ -83,17 +86,20 @@ describe('ReactHooksInspection', () => { let tree = ReactDebugTools.inspectHooks(Foo, {}); expect(tree).toEqual([ { + isEditable: false, index: -1, name: 'Custom', value: undefined, subHooks: [ { + isEditable: true, index: 0, name: 'State', subHooks: [], value: 'hello', }, { + isEditable: false, index: 1, name: 'Effect', subHooks: [], @@ -102,17 +108,20 @@ describe('ReactHooksInspection', () => { ], }, { + isEditable: false, index: -1, name: 'Custom', value: undefined, subHooks: [ { + isEditable: true, index: 2, name: 'State', value: 'world', subHooks: [], }, { + isEditable: false, index: 3, name: 'Effect', value: effect, @@ -152,22 +161,26 @@ describe('ReactHooksInspection', () => { let tree = ReactDebugTools.inspectHooks(Foo, {}); expect(tree).toEqual([ { + isEditable: false, index: -1, name: 'Bar', value: undefined, subHooks: [ { + isEditable: false, index: -1, name: 'Custom', value: undefined, subHooks: [ { + isEditable: true, index: 0, name: 'Reducer', value: 'hello', subHooks: [], }, { + isEditable: false, index: 1, name: 'Effect', value: effect, @@ -176,6 +189,7 @@ describe('ReactHooksInspection', () => { ], }, { + isEditable: false, index: 2, name: 'LayoutEffect', value: effect, @@ -184,27 +198,32 @@ describe('ReactHooksInspection', () => { ], }, { + isEditable: false, index: -1, name: 'Baz', value: undefined, subHooks: [ { + isEditable: false, index: 3, name: 'LayoutEffect', value: effect, subHooks: [], }, { + isEditable: false, index: -1, name: 'Custom', subHooks: [ { + isEditable: true, index: 4, name: 'Reducer', subHooks: [], value: 'world', }, { + isEditable: false, index: 5, name: 'Effect', subHooks: [], @@ -227,6 +246,7 @@ describe('ReactHooksInspection', () => { let tree = ReactDebugTools.inspectHooks(Foo, {}); expect(tree).toEqual([ { + isEditable: false, index: 0, name: 'Context', value: 'default', @@ -290,10 +310,13 @@ describe('ReactHooksInspection', () => { let tree = ReactDebugTools.inspectHooks(Foo, {}); expect(tree).toEqual([ { + isEditable: false, index: -1, name: 'Custom', value: __DEV__ ? 'bar:123' : undefined, - subHooks: [{index: 0, name: 'State', subHooks: [], value: 0}], + subHooks: [ + {isEditable: true, index: 0, name: 'State', subHooks: [], value: 0}, + ], }, ]); }); diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js index 55518dc7d393..bd6578a33174 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js @@ -40,8 +40,8 @@ describe('ReactHooksInspectionIntegration', () => { let childFiber = renderer.root.findByType(Foo)._currentFiber(); let tree = ReactDebugTools.inspectHooksOfFiber(childFiber); expect(tree).toEqual([ - {index: 0, name: 'State', value: 'hello', subHooks: []}, - {index: 1, name: 'State', value: 'world', subHooks: []}, + {isEditable: true, index: 0, name: 'State', value: 'hello', subHooks: []}, + {isEditable: true, index: 1, name: 'State', value: 'world', subHooks: []}, ]); let { @@ -55,8 +55,8 @@ describe('ReactHooksInspectionIntegration', () => { tree = ReactDebugTools.inspectHooksOfFiber(childFiber); expect(tree).toEqual([ - {index: 0, name: 'State', value: 'Hi', subHooks: []}, - {index: 1, name: 'State', value: 'world', subHooks: []}, + {isEditable: true, index: 0, name: 'State', value: 'Hi', subHooks: []}, + {isEditable: true, index: 1, name: 'State', value: 'world', subHooks: []}, ]); act(() => setStateB('world!')); @@ -65,8 +65,14 @@ describe('ReactHooksInspectionIntegration', () => { tree = ReactDebugTools.inspectHooksOfFiber(childFiber); expect(tree).toEqual([ - {index: 0, name: 'State', value: 'Hi', subHooks: []}, - {index: 1, name: 'State', value: 'world!', subHooks: []}, + {isEditable: true, index: 0, name: 'State', value: 'Hi', subHooks: []}, + { + isEditable: true, + index: 1, + name: 'State', + value: 'world!', + subHooks: [], + }, ]); }); @@ -116,19 +122,38 @@ describe('ReactHooksInspectionIntegration', () => { let tree = ReactDebugTools.inspectHooksOfFiber(childFiber); expect(tree).toEqual([ - {index: 0, name: 'State', value: 'a', subHooks: []}, - {index: 1, name: 'Reducer', value: 'b', subHooks: []}, - {index: 2, name: 'Ref', value: 'c', subHooks: []}, - {index: 3, name: 'LayoutEffect', value: effect, subHooks: []}, - {index: 4, name: 'Effect', value: effect, subHooks: []}, + {isEditable: true, index: 0, name: 'State', value: 'a', subHooks: []}, + {isEditable: true, index: 1, name: 'Reducer', value: 'b', subHooks: []}, + {isEditable: false, index: 2, name: 'Ref', value: 'c', subHooks: []}, + { + isEditable: false, + index: 3, + name: 'LayoutEffect', + value: effect, + subHooks: [], + }, { + isEditable: false, + index: 4, + name: 'Effect', + value: effect, + subHooks: [], + }, + { + isEditable: false, index: 5, name: 'ImperativeHandle', value: outsideRef.current, subHooks: [], }, - {index: 6, name: 'Memo', value: 'ab', subHooks: []}, - {index: 7, name: 'Callback', value: updateStates, subHooks: []}, + {isEditable: false, index: 6, name: 'Memo', value: 'ab', subHooks: []}, + { + isEditable: false, + index: 7, + name: 'Callback', + value: updateStates, + subHooks: [], + }, ]); updateStates(); @@ -137,19 +162,38 @@ describe('ReactHooksInspectionIntegration', () => { tree = ReactDebugTools.inspectHooksOfFiber(childFiber); expect(tree).toEqual([ - {index: 0, name: 'State', value: 'A', subHooks: []}, - {index: 1, name: 'Reducer', value: 'B', subHooks: []}, - {index: 2, name: 'Ref', value: 'C', subHooks: []}, - {index: 3, name: 'LayoutEffect', value: effect, subHooks: []}, - {index: 4, name: 'Effect', value: effect, subHooks: []}, + {isEditable: true, index: 0, name: 'State', value: 'A', subHooks: []}, + {isEditable: true, index: 1, name: 'Reducer', value: 'B', subHooks: []}, + {isEditable: false, index: 2, name: 'Ref', value: 'C', subHooks: []}, { + isEditable: false, + index: 3, + name: 'LayoutEffect', + value: effect, + subHooks: [], + }, + { + isEditable: false, + index: 4, + name: 'Effect', + value: effect, + subHooks: [], + }, + { + isEditable: false, index: 5, name: 'ImperativeHandle', value: outsideRef.current, subHooks: [], }, - {index: 6, name: 'Memo', value: 'Ab', subHooks: []}, - {index: 7, name: 'Callback', value: updateStates, subHooks: []}, + {isEditable: false, index: 6, name: 'Memo', value: 'Ab', subHooks: []}, + { + isEditable: false, + index: 7, + name: 'Callback', + value: updateStates, + subHooks: [], + }, ]); }); @@ -168,6 +212,7 @@ describe('ReactHooksInspectionIntegration', () => { let tree = ReactDebugTools.inspectHooksOfFiber(childFiber); expect(tree).toEqual([ { + isEditable: false, index: 0, name: 'Context', value: 'contextual', @@ -188,7 +233,13 @@ describe('ReactHooksInspectionIntegration', () => { let childFiber = renderer.root.findByType(Foo)._currentFiber(); let tree = ReactDebugTools.inspectHooksOfFiber(childFiber); expect(tree).toEqual([ - {index: 0, name: 'ImperativeHandle', value: obj, subHooks: []}, + { + isEditable: false, + index: 0, + name: 'ImperativeHandle', + value: obj, + subHooks: [], + }, ]); }); @@ -203,7 +254,7 @@ describe('ReactHooksInspectionIntegration', () => { let childFiber = renderer.root.findByType(InnerFoo)._currentFiber(); let tree = ReactDebugTools.inspectHooksOfFiber(childFiber); expect(tree).toEqual([ - {index: 0, name: 'State', value: 'hello', subHooks: []}, + {isEditable: true, index: 0, name: 'State', value: 'hello', subHooks: []}, ]); }); @@ -221,10 +272,19 @@ describe('ReactHooksInspectionIntegration', () => { let tree = ReactDebugTools.inspectHooksOfFiber(childFiber); expect(tree).toEqual([ { + isEditable: false, index: -1, name: 'Custom', value: undefined, - subHooks: [{index: 0, name: 'State', value: 'hello', subHooks: []}], + subHooks: [ + { + isEditable: true, + index: 0, + name: 'State', + value: 'hello', + subHooks: [], + }, + ], }, ]); }); @@ -252,28 +312,56 @@ describe('ReactHooksInspectionIntegration', () => { let tree = ReactDebugTools.inspectHooksOfFiber(childFiber); expect(tree).toEqual([ { + isEditable: false, index: -1, name: 'LabeledValue', value: __DEV__ ? 'custom label a' : undefined, - subHooks: [{index: 0, name: 'State', value: 'a', subHooks: []}], + subHooks: [ + { + isEditable: true, + index: 0, + name: 'State', + value: 'a', + subHooks: [], + }, + ], }, { + isEditable: true, index: 1, name: 'State', value: 'b', subHooks: [], }, { + isEditable: false, index: -1, name: 'Anonymous', value: undefined, - subHooks: [{index: 2, name: 'State', value: 'c', subHooks: []}], + subHooks: [ + { + isEditable: true, + index: 2, + name: 'State', + value: 'c', + subHooks: [], + }, + ], }, { + isEditable: false, index: -1, name: 'LabeledValue', value: __DEV__ ? 'custom label d' : undefined, - subHooks: [{index: 3, name: 'State', value: 'd', subHooks: []}], + subHooks: [ + { + isEditable: true, + index: 3, + name: 'State', + value: 'd', + subHooks: [], + }, + ], }, ]); }); @@ -296,15 +384,25 @@ describe('ReactHooksInspectionIntegration', () => { let tree = ReactDebugTools.inspectHooksOfFiber(childFiber); expect(tree).toEqual([ { + isEditable: false, index: -1, name: 'Outer', value: __DEV__ ? 'outer' : undefined, subHooks: [ { + isEditable: false, index: -1, name: 'Inner', value: __DEV__ ? 'inner' : undefined, - subHooks: [{index: 0, name: 'State', value: 0, subHooks: []}], + subHooks: [ + { + isEditable: true, + index: 0, + name: 'State', + value: 0, + subHooks: [], + }, + ], }, ], }, @@ -333,22 +431,31 @@ describe('ReactHooksInspectionIntegration', () => { let tree = ReactDebugTools.inspectHooksOfFiber(childFiber); expect(tree).toEqual([ { + isEditable: false, index: -1, name: 'SingleLabelCustom', value: __DEV__ ? 'single one' : undefined, - subHooks: [{index: 0, name: 'State', value: 0, subHooks: []}], + subHooks: [ + {isEditable: true, index: 0, name: 'State', value: 0, subHooks: []}, + ], }, { + isEditable: false, index: -1, name: 'MultiLabelCustom', value: __DEV__ ? ['one', 'two', 'three'] : undefined, - subHooks: [{index: 1, name: 'State', value: 0, subHooks: []}], + subHooks: [ + {isEditable: true, index: 1, name: 'State', value: 0, subHooks: []}, + ], }, { + isEditable: false, index: -1, name: 'SingleLabelCustom', value: __DEV__ ? 'single two' : undefined, - subHooks: [{index: 2, name: 'State', value: 0, subHooks: []}], + subHooks: [ + {isEditable: true, index: 2, name: 'State', value: 0, subHooks: []}, + ], }, ]); }); @@ -378,10 +485,13 @@ describe('ReactHooksInspectionIntegration', () => { let tree = ReactDebugTools.inspectHooksOfFiber(childFiber); expect(tree).toEqual([ { + isEditable: false, index: -1, name: 'Custom', value: __DEV__ ? 'bar:123' : undefined, - subHooks: [{index: 0, name: 'State', subHooks: [], value: 0}], + subHooks: [ + {isEditable: true, index: 0, name: 'State', subHooks: [], value: 0}, + ], }, ]); }); @@ -415,7 +525,7 @@ describe('ReactHooksInspectionIntegration', () => { let childFiber = renderer.root._currentFiber(); let tree = ReactDebugTools.inspectHooksOfFiber(childFiber); expect(tree).toEqual([ - {index: 0, name: 'State', value: 'def', subHooks: []}, + {isEditable: true, index: 0, name: 'State', value: 'def', subHooks: []}, ]); }); diff --git a/packages/react-reconciler/src/ReactFiberReconciler.js b/packages/react-reconciler/src/ReactFiberReconciler.js index 5b45cc5894bf..106fe645c04d 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.js @@ -369,7 +369,7 @@ if (__DEV__) { return copyWithSetImpl(obj, path, 0, value); }; - // Support DevTools editable hooks state. + // Support DevTools editable values for useState and useReducer. overrideHook = ( fiber: Fiber, index: number, @@ -382,8 +382,13 @@ if (__DEV__) { index--; } if (currentHook !== null) { - let updatedState = copyWithSet(currentHook.memoizedState, path, value); - currentHook.queue.dispatch(updatedState); + flushPassiveEffects(); + currentHook.memoizedState = copyWithSet( + currentHook.memoizedState, + path, + value, + ); + scheduleWork(fiber, Sync); } };