@samitouri / QOS-React-2 / commits / 191920605f

[Fresh] Implement missing features (#15860)

* Fix existing test This test included a change in variable name. It wasn't needed, and distracts from the actual thing being tested (the annotation). * Reset state on edits to initial state argument * Add a way to check whether there are hot updates prepareUpdate() now returns null if there are none. * Add a way to query host nodes for families

Dan Abramov committed Jun 11, 2019 at 19:37 UTC 191920605fa88949e640dbf6da4f8659020a4566
7 files changed +407 -37
packages/react-fresh/src/ReactFreshBabelPlugin.js
+11
@@ -175,6 +175,17 @@ export default function(babel) {
175 // TODO: if there is no LHS, consider some other heuristic.
176 key = hookCallPath.parentPath.get('id').getSource();
177 }
178 +
179 + // Some built-in Hooks reset on edits to arguments.
180 + const args = hookCallPath.get('arguments');
181 + if (hookName === 'useState' && args.length > 0) {
182 + // useState second argument is initial state.
183 + key += '(' + args[0].getSource() + ')';
184 + } else if (hookName === 'useReducer' && args.length > 1) {
185 + // useReducer second argument is initial state.
186 + key += '(' + args[1].getSource() + ')';
187 + }
188 +
189 hookCallsForFn.push({
190 name: hookName,
191 callee: hookCallPath.node.callee,
packages/react-fresh/src/ReactFreshRuntime.js
+9 -1
@@ -115,7 +115,11 @@ function resolveFamily(type) {
115 return familiesByType.get(type);
116 }
117
118 -export function prepareUpdate(): HotUpdate {
118 +export function prepareUpdate(): HotUpdate | null {
119 + if (pendingUpdates.length === 0) {
120 + return null;
121 + }
122 +
123 const staleFamilies = new Set();
124 const updatedFamilies = new Set();
125
@@ -206,3 +210,7 @@ export function collectCustomHooksForSignature(type: any) {
210 computeFullKey(signature);
211 }
212 }
213 +
214 +export function getFamilyByID(id: string): Family | void {
215 + return allFamiliesByID.get(id);
216 +}
packages/react-fresh/src/__tests__/ReactFresh-test.js
+110
@@ -20,6 +20,7 @@ let act;
20 describe('ReactFresh', () => {
21 let container;
22 let lastRoot;
23 + let findHostNodesForHotUpdate;
24 let scheduleHotUpdate;
25
26 beforeEach(() => {
@@ -27,6 +28,7 @@ describe('ReactFresh', () => {
28 supportsFiber: true,
29 inject: injected => {
30 scheduleHotUpdate = injected.scheduleHotUpdate;
31 + findHostNodesForHotUpdate = injected.findHostNodesForHotUpdate;
32 },
33 onCommitFiberRoot: (id, root) => {
34 lastRoot = root;
@@ -2934,4 +2936,112 @@ describe('ReactFresh', () => {
2936 expect(finalEl.textContent).toBe('1');
2937 }
2938 });
2939 +
2940 + it('can find host nodes for a family', () => {
2941 + if (__DEV__) {
2942 + render(() => {
2943 + function Child({children}) {
2944 + return <div className="Child">{children}</div>;
2945 + }
2946 + __register__(Child, 'Child');
2947 +
2948 + function Parent({children}) {
2949 + return (
2950 + <div className="Parent">
2951 + <div>
2952 + <Child />
2953 + </div>
2954 + <div>
2955 + <Child />
2956 + </div>
2957 + </div>
2958 + );
2959 + }
2960 + __register__(Parent, 'Parent');
2961 +
2962 + function App() {
2963 + return (
2964 + <div className="App">
2965 + <Parent />
2966 + <Cls>
2967 + <Parent />
2968 + </Cls>
2969 + <Indirection>
2970 + <Empty />
2971 + </Indirection>
2972 + </div>
2973 + );
2974 + }
2975 + __register__(App, 'App');
2976 +
2977 + class Cls extends React.Component {
2978 + render() {
2979 + return this.props.children;
2980 + }
2981 + }
2982 +
2983 + function Indirection({children}) {
2984 + return children;
2985 + }
2986 +
2987 + function Empty() {
2988 + return null;
2989 + }
2990 + __register__(Empty, 'Empty');
2991 +
2992 + function Frag() {
2993 + return (
2994 + <React.Fragment>
2995 + <div className="Frag">
2996 + <div />
2997 + </div>
2998 + <div className="Frag">
2999 + <div />
3000 + </div>
3001 + </React.Fragment>
3002 + );
3003 + }
3004 + __register__(Frag, 'Frag');
3005 +
3006 + return App;
3007 + });
3008 +
3009 + const parentFamily = ReactFreshRuntime.getFamilyByID('Parent');
3010 + const childFamily = ReactFreshRuntime.getFamilyByID('Child');
3011 + const emptyFamily = ReactFreshRuntime.getFamilyByID('Empty');
3012 +
3013 + testFindNodesForFamilies(
3014 + [parentFamily],
3015 + container.querySelectorAll('.Parent'),
3016 + );
3017 +
3018 + testFindNodesForFamilies(
3019 + [childFamily],
3020 + container.querySelectorAll('.Child'),
3021 + );
3022 +
3023 + // When searching for both Parent and Child,
3024 + // we'll stop visual highlighting at the Parent.
3025 + testFindNodesForFamilies(
3026 + [parentFamily, childFamily],
3027 + container.querySelectorAll('.Parent'),
3028 + );
3029 +
3030 + // When we can't find host nodes, use the closest parent.
3031 + testFindNodesForFamilies(
3032 + [emptyFamily],
3033 + container.querySelectorAll('.App'),
3034 + );
3035 + }
3036 + });
3037 +
3038 + function testFindNodesForFamilies(families, expectedNodes) {
3039 + const foundNodes = Array.from(
3040 + findHostNodesForHotUpdate(lastRoot, families),
3041 + );
3042 + expect(foundNodes.length).toEqual(expectedNodes.length);
3043 + foundNodes.forEach((node, i) => {
3044 + expect(node).toBe(expectedNodes[i]);
3045 + });
3046 + }
3047 });
packages/react-fresh/src/__tests__/ReactFreshIntegration-test.js
+131 -29
@@ -73,6 +73,8 @@ describe('ReactFreshIntegration', () => {
73 act(() => {
74 ReactDOM.render(<Component />, container);
75 });
76 + // Module initialization shouldn't be counted as a hot update.
77 + expect(ReactFreshRuntime.prepareUpdate()).toBe(null);
78 }
79
80 function patch(source) {
@@ -275,9 +277,10 @@ describe('ReactFreshIntegration', () => {
277 if (__DEV__) {
278 render(`
279 const {useState} = React;
280 + const S = 1;
281
282 export default function App() {
280 - const [foo, setFoo] = useState(1);
283 + const [foo, setFoo] = useState(S);
284 return <h1>A{foo}</h1>;
285 }
286 `);
@@ -286,9 +289,10 @@ describe('ReactFreshIntegration', () => {
289
290 patch(`
291 const {useState} = React;
292 + const S = 2;
293
294 export default function App() {
291 - const [foo, setFoo] = useState('ignored');
295 + const [foo, setFoo] = useState(S);
296 return <h1>B{foo}</h1>;
297 }
298 `);
@@ -298,16 +302,17 @@ describe('ReactFreshIntegration', () => {
302
303 patch(`
304 const {useState} = React;
305 + const S = 3;
306
307 export default function App() {
303 - const [bar, setBar] = useState(2);
308 + const [bar, setBar] = useState(S);
309 return <h1>C{bar}</h1>;
310 }
311 `);
312 // Different state variable name, so state is reset.
313 expect(container.firstChild).not.toBe(el);
314 const newEl = container.firstChild;
310 - expect(newEl.textContent).toBe('C2');
315 + expect(newEl.textContent).toBe('C3');
316 }
317 });
318
@@ -315,10 +320,11 @@ describe('ReactFreshIntegration', () => {
320 if (__DEV__) {
321 render(`
322 const {useState} = React;
323 + const S = 1;
324
325 function hoc(Wrapped) {
326 return function Generated() {
321 - const [foo, setFoo] = useState(1);
327 + const [foo, setFoo] = useState(S);
328 return <Wrapped value={foo} />;
329 };
330 }
@@ -332,10 +338,11 @@ describe('ReactFreshIntegration', () => {
338
339 patch(`
340 const {useState} = React;
341 + const S = 2;
342
343 function hoc(Wrapped) {
344 return function Generated() {
338 - const [foo, setFoo] = useState('ignored');
345 + const [foo, setFoo] = useState(S);
346 return <Wrapped value={foo} />;
347 };
348 }
@@ -350,10 +357,11 @@ describe('ReactFreshIntegration', () => {
357
358 patch(`
359 const {useState} = React;
360 + const S = 3;
361
362 function hoc(Wrapped) {
363 return function Generated() {
356 - const [bar, setBar] = useState(2);
364 + const [bar, setBar] = useState(S);
365 return <Wrapped value={bar} />;
366 };
367 }
@@ -365,7 +373,7 @@ describe('ReactFreshIntegration', () => {
373 // Different state variable name, so state is reset.
374 expect(container.firstChild).not.toBe(el);
375 const newEl = container.firstChild;
368 - expect(newEl.textContent).toBe('C2');
376 + expect(newEl.textContent).toBe('C3');
377 }
378 });
379
@@ -373,10 +381,11 @@ describe('ReactFreshIntegration', () => {
381 if (__DEV__) {
382 render(`
383 const {useState} = React;
384 + const S = 1;
385
386 function hoc(Wrapped) {
387 return function Generated() {
379 - const [foo, setFoo] = useState(1);
388 + const [foo, setFoo] = useState(S);
389 return <Wrapped value={foo} />;
390 };
391 }
@@ -392,10 +401,11 @@ describe('ReactFreshIntegration', () => {
401
402 patch(`
403 const {useState} = React;
404 + const S = 2;
405
406 function hoc(Wrapped) {
407 return function Generated() {
398 - const [foo, setFoo] = useState('ignored');
408 + const [foo, setFoo] = useState(S);
409 return <Wrapped value={foo} />;
410 };
411 }
@@ -412,10 +422,11 @@ describe('ReactFreshIntegration', () => {
422
423 patch(`
424 const {useState} = React;
425 + const S = 3;
426
427 function hoc(Wrapped) {
428 return function Generated() {
418 - const [bar, setBar] = useState(2);
429 + const [bar, setBar] = useState(S);
430 return <Wrapped value={bar} />;
431 };
432 }
@@ -429,7 +440,7 @@ describe('ReactFreshIntegration', () => {
440 // Different state variable name, so state is reset.
441 expect(container.firstChild).not.toBe(el);
442 const newEl = container.firstChild;
432 - expect(newEl.textContent).toBe('C2');
443 + expect(newEl.textContent).toBe('C3');
444 }
445 });
446
@@ -757,9 +768,10 @@ describe('ReactFreshIntegration', () => {
768 if (__DEV__) {
769 render(`
770 const {useState} = React;
771 + const S = 1;
772
773 export default function App() {
762 - const [foo, setFoo] = useState(1);
774 + const [foo, setFoo] = useState(S);
775 return <h1>A{foo}</h1>;
776 }
777 `);
@@ -768,9 +780,10 @@ describe('ReactFreshIntegration', () => {
780
781 patch(`
782 const {useState} = React;
783 + const S = 2;
784
785 export default function App() {
773 - const [foo, setFoo] = useState('ignored');
786 + const [foo, setFoo] = useState(S);
787 return <h1>B{foo}</h1>;
788 }
789 `);
@@ -780,75 +793,164 @@ describe('ReactFreshIntegration', () => {
793
794 patch(`
795 const {useState} = React;
796 + const S = 3;
797
798 /* @hot reset */
799
800 export default function App() {
787 - const [bar, setBar] = useState(2);
788 - return <h1>C{bar}</h1>;
801 + const [foo, setFoo] = useState(S);
802 + return <h1>C{foo}</h1>;
803 }
804 `);
805 // Found remount annotation, so state is reset.
806 expect(container.firstChild).not.toBe(el);
807 el = container.firstChild;
794 - expect(el.textContent).toBe('C2');
808 + expect(el.textContent).toBe('C3');
809
810 patch(`
811 const {useState} = React;
812 + const S = 4;
813
814 export default function App() {
815
816 // @hot reset
817
803 - const [bar, setBar] = useState(3);
804 - return <h1>D{bar}</h1>;
818 + const [foo, setFoo] = useState(S);
819 + return <h1>D{foo}</h1>;
820 }
821 `);
822 // Found remount annotation, so state is reset.
823 expect(container.firstChild).not.toBe(el);
824 el = container.firstChild;
810 - expect(el.textContent).toBe('D3');
825 + expect(el.textContent).toBe('D4');
826
827 patch(`
828 const {useState} = React;
829 + const S = 5;
830
831 export default function App() {
816 - const [bar, setBar] = useState(4);
817 - return <h1>E{bar}</h1>;
832 + const [foo, setFoo] = useState(S);
833 + return <h1>E{foo}</h1>;
834 }
835 `);
836 // There is no remount annotation anymore,
837 // so preserve the previous state.
838 expect(container.firstChild).toBe(el);
823 - expect(el.textContent).toBe('E3');
839 + expect(el.textContent).toBe('E4');
840
841 patch(`
842 const {useState} = React;
843 + const S = 6;
844
845 export default function App() {
829 - const [bar, setBar] = useState(4);
830 - return <h1>F{bar}</h1>;
846 + const [foo, setFoo] = useState(S);
847 + return <h1>F{foo}</h1>;
848 }
849 `);
850 // Continue editing.
851 expect(container.firstChild).toBe(el);
835 - expect(el.textContent).toBe('F3');
852 + expect(el.textContent).toBe('F4');
853
854 patch(`
855 const {useState} = React;
856 + const S = 7;
857
858 export default function App() {
859
860 /* @hot reset */
861
844 - const [bar, setBar] = useState(5);
845 - return <h1>G{bar}</h1>;
862 + const [foo, setFoo] = useState(S);
863 + return <h1>G{foo}</h1>;
864 }
865 `);
866 // Force remount one last time.
867 expect(container.firstChild).not.toBe(el);
868 el = container.firstChild;
851 - expect(el.textContent).toBe('G5');
869 + expect(el.textContent).toBe('G7');
870 + }
871 + });
872 +
873 + // This is best effort for simple cases.
874 + // We won't attempt to resolve identifiers.
875 + it('resets state when useState initial state is edited', () => {
876 + if (__DEV__) {
877 + render(`
878 + const {useState} = React;
879 +
880 + export default function App() {
881 + const [foo, setFoo] = useState(1);
882 + return <h1>A{foo}</h1>;
883 + }
884 + `);
885 + let el = container.firstChild;
886 + expect(el.textContent).toBe('A1');
887 +
888 + patch(`
889 + const {useState} = React;
890 +
891 + export default function App() {
892 + const [foo, setFoo] = useState(1);
893 + return <h1>B{foo}</h1>;
894 + }
895 + `);
896 + // Same initial state, so it's preserved.
897 + expect(container.firstChild).toBe(el);
898 + expect(el.textContent).toBe('B1');
899 +
900 + patch(`
901 + const {useState} = React;
902 +
903 + export default function App() {
904 + const [foo, setFoo] = useState(2);
905 + return <h1>C{foo}</h1>;
906 + }
907 + `);
908 + // Different initial state, so state is reset.
909 + expect(container.firstChild).not.toBe(el);
910 + el = container.firstChild;
911 + expect(el.textContent).toBe('C2');
912 + }
913 + });
914 +
915 + // This is best effort for simple cases.
916 + // We won't attempt to resolve identifiers.
917 + it('resets state when useReducer initial state is edited', () => {
918 + if (__DEV__) {
919 + render(`
920 + const {useReducer} = React;
921 +
922 + export default function App() {
923 + const [foo, setFoo] = useReducer(x => x, 1);
924 + return <h1>A{foo}</h1>;
925 + }
926 + `);
927 + let el = container.firstChild;
928 + expect(el.textContent).toBe('A1');
929 +
930 + patch(`
931 + const {useReducer} = React;
932 +
933 + export default function App() {
934 + const [foo, setFoo] = useReducer(x => x, 1);
935 + return <h1>B{foo}</h1>;
936 + }
937 + `);
938 + // Same initial state, so it's preserved.
939 + expect(container.firstChild).toBe(el);
940 + expect(el.textContent).toBe('B1');
941 +
942 + patch(`
943 + const {useReducer} = React;
944 +
945 + export default function App() {
946 + const [foo, setFoo] = useReducer(x => x, 2);
947 + return <h1>C{foo}</h1>;
948 + }
949 + `);
950 + // Different initial state, so state is reset.
951 + expect(container.firstChild).not.toBe(el);
952 + el = container.firstChild;
953 + expect(el.textContent).toBe('C2');
954 }
955 });
956
packages/react-fresh/src/__tests__/__snapshots__/ReactFreshBabelPlugin-test.js.snap
+6 -6
@@ -11,7 +11,7 @@ export default function App() {
11 return <h1>{foo}</h1>;
12 }
13
14 -_s(App, "useState{[foo, setFoo]}\\nuseEffect{}");
14 +_s(App, "useState{[foo, setFoo](0)}\\nuseEffect{}");
15
16 _c = App;
17
@@ -30,7 +30,7 @@ export const A = _c3 = React.memo(_c2 = React.forwardRef(_c = _s((props, ref) =>
30 const [foo, setFoo] = useState(0);
31 React.useEffect(() => {});
32 return <h1 ref={ref}>{foo}</h1>;
33 -}, "useState{[foo, setFoo]}\\nuseEffect{}")));
33 +}, "useState{[foo, setFoo](0)}\\nuseEffect{}")));
34
35 export const B = _c6 = React.memo(_c5 = React.forwardRef(_c4 = _s2(function (props, ref) {
36 _s2();
@@ -38,7 +38,7 @@ export const B = _c6 = React.memo(_c5 = React.forwardRef(_c4 = _s2(function (pro
38 const [foo, setFoo] = useState(0);
39 React.useEffect(() => {});
40 return <h1 ref={ref}>{foo}</h1>;
41 -}, "useState{[foo, setFoo]}\\nuseEffect{}")));
41 +}, "useState{[foo, setFoo](0)}\\nuseEffect{}")));
42
43 function hoc() {
44 var _s3 = __signature__();
@@ -49,7 +49,7 @@ function hoc() {
49 const [foo, setFoo] = useState(0);
50 React.useEffect(() => {});
51 return <h1 ref={ref}>{foo}</h1>;
52 - }, "useState{[foo, setFoo]}\\nuseEffect{}");
52 + }, "useState{[foo, setFoo](0)}\\nuseEffect{}");
53 }
54
55 export let C = hoc();
@@ -87,7 +87,7 @@ export default function App() {
87 return foo;
88 }
89
90 - _s(useFancyState, 'useState{[foo, setFoo]}\\nuseFancyEffect{}', true);
90 + _s(useFancyState, 'useState{[foo, setFoo](0)}\\nuseFancyEffect{}', true);
91
92 const bar = useFancyState();
93 const baz = FancyHook.useThing();
@@ -169,7 +169,7 @@ function useFancyState() {
169 return foo;
170 }
171
172 -_s(useFancyState, "useState{[foo, setFoo]}\\nuseFancyEffect{}", false, () => [useFancyEffect]);
172 +_s(useFancyState, "useState{[foo, setFoo](0)}\\nuseFancyEffect{}", false, () => [useFancyEffect]);
173
174 const useFancyEffect = () => {
175 _s2();
packages/react-reconciler/src/ReactFiberHotReloading.js
+135
@@ -10,6 +10,7 @@
10 import type {ReactElement} from 'shared/ReactElementType';
11 import type {Fiber} from './ReactFiber';
12 import type {FiberRoot} from './ReactFiberRoot';
13 +import type {Instance} from './ReactFiberHostConfig';
14
15 import {
16 flushSync,
@@ -21,6 +22,9 @@ import {
22 ClassComponent,
23 FunctionComponent,
24 ForwardRef,
25 + HostComponent,
26 + HostPortal,
27 + HostRoot,
28 MemoComponent,
29 SimpleMemoComponent,
30 } from 'shared/ReactWorkTags';
@@ -287,3 +291,134 @@ function scheduleFibersWithFamiliesRecursively(
291 }
292 }
293 }
294 +
295 +export function findHostNodesForHotUpdate(
296 + root: FiberRoot,
297 + families: Array<Family>,
298 +): Set<Instance> {
299 + if (__DEV__) {
300 + const hostNodes = new Set();
301 + const types = new Set(families.map(family => family.current));
302 + findHostNodesForMatchingFibersRecursively(root.current, types, hostNodes);
303 + return hostNodes;
304 + } else {
305 + throw new Error(
306 + 'Did not expect findHostNodesForHotUpdate to be called in production.',
307 + );
308 + }
309 +}
310 +
311 +function findHostNodesForMatchingFibersRecursively(
312 + fiber: Fiber,
313 + types: Set<any>,
314 + hostNodes: Set<Instance>,
315 +) {
316 + if (__DEV__) {
317 + const {child, sibling, tag, type} = fiber;
318 +
319 + let candidateType = null;
320 + switch (tag) {
321 + case FunctionComponent:
322 + case SimpleMemoComponent:
323 + case ClassComponent:
324 + candidateType = type;
325 + break;
326 + case ForwardRef:
327 + candidateType = type.render;
328 + break;
329 + default:
330 + break;
331 + }
332 +
333 + let didMatch = false;
334 + if (candidateType !== null) {
335 + if (types.has(candidateType)) {
336 + didMatch = true;
337 + }
338 + }
339 +
340 + if (didMatch) {
341 + // We have a match. This only drills down to the closest host components.
342 + // There's no need to search deeper because for the purpose of giving
343 + // visual feedback, "flashing" outermost parent rectangles is sufficient.
344 + findHostNodesForFiberShallowly(fiber, hostNodes);
345 + } else {
346 + // If there's no match, maybe there will be one further down in the child tree.
347 + if (child !== null) {
348 + findHostNodesForMatchingFibersRecursively(child, types, hostNodes);
349 + }
350 + }
351 +
352 + if (sibling !== null) {
353 + findHostNodesForMatchingFibersRecursively(sibling, types, hostNodes);
354 + }
355 + }
356 +}
357 +
358 +function findHostNodesForFiberShallowly(
359 + fiber: Fiber,
360 + hostNodes: Set<Instance>,
361 +): void {
362 + if (__DEV__) {
363 + const foundHostNodes = findChildHostNodesForFiberShallowly(
364 + fiber,
365 + hostNodes,
366 + );
367 + if (foundHostNodes) {
368 + return;
369 + }
370 + // If we didn't find any host children, fallback to closest host parent.
371 + let node = fiber;
372 + while (true) {
373 + switch (node.tag) {
374 + case HostComponent:
375 + hostNodes.add(node.stateNode);
376 + return;
377 + case HostPortal:
378 + hostNodes.add(node.stateNode.containerInfo);
379 + return;
380 + case HostRoot:
381 + hostNodes.add(node.stateNode.containerInfo);
382 + return;
383 + }
384 + if (node.return === null) {
385 + throw new Error('Expected to reach root first.');
386 + }
387 + node = node.return;
388 + }
389 + }
390 +}
391 +
392 +function findChildHostNodesForFiberShallowly(
393 + fiber: Fiber,
394 + hostNodes: Set<Instance>,
395 +): boolean {
396 + if (__DEV__) {
397 + let node: Fiber = fiber;
398 + let foundHostNodes = false;
399 + while (true) {
400 + if (node.tag === HostComponent) {
401 + // We got a match.
402 + foundHostNodes = true;
403 + hostNodes.add(node.stateNode);
404 + // There may still be more, so keep searching.
405 + } else if (node.child !== null) {
406 + node.child.return = node;
407 + node = node.child;
408 + continue;
409 + }
410 + if (node === fiber) {
411 + return foundHostNodes;
412 + }
413 + while (node.sibling === null) {
414 + if (node.return === null || node.return === fiber) {
415 + return foundHostNodes;
416 + }
417 + node = node.return;
418 + }
419 + node.sibling.return = node.return;
420 + node = node.sibling;
421 + }
422 + }
423 + return false;
424 +}
packages/react-reconciler/src/ReactFiberReconciler.js
+5 -1
@@ -70,7 +70,10 @@ import {StrictMode} from './ReactTypeOfMode';
70 import {Sync} from './ReactFiberExpirationTime';
71 import {revertPassiveEffectsChange} from 'shared/ReactFeatureFlags';
72 import {requestCurrentSuspenseConfig} from './ReactFiberSuspenseConfig';
73 -import {scheduleHotUpdate} from './ReactFiberHotReloading';
73 +import {
74 + scheduleHotUpdate,
75 + findHostNodesForHotUpdate,
76 +} from './ReactFiberHotReloading';
77
78 type OpaqueRoot = FiberRoot;
79
@@ -472,6 +475,7 @@ export function injectIntoDevTools(devToolsConfig: DevToolsConfig): boolean {
475
476 return injectInternals({
477 ...devToolsConfig,
478 + findHostNodesForHotUpdate: __DEV__ ? findHostNodesForHotUpdate : null,
479 scheduleHotUpdate: __DEV__ ? scheduleHotUpdate : null,
480 overrideHookState,
481 overrideProps,