Skip to content

Commit 1919206

Browse files
authored
[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
1 parent c403ae4 commit 1919206

File tree

7 files changed

+407
-37
lines changed

7 files changed

+407
-37
lines changed

packages/react-fresh/src/ReactFreshBabelPlugin.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,17 @@ export default function(babel) {
175175
// TODO: if there is no LHS, consider some other heuristic.
176176
key = hookCallPath.parentPath.get('id').getSource();
177177
}
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+
178189
hookCallsForFn.push({
179190
name: hookName,
180191
callee: hookCallPath.node.callee,

packages/react-fresh/src/ReactFreshRuntime.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,11 @@ function resolveFamily(type) {
115115
return familiesByType.get(type);
116116
}
117117

118-
export function prepareUpdate(): HotUpdate {
118+
export function prepareUpdate(): HotUpdate | null {
119+
if (pendingUpdates.length === 0) {
120+
return null;
121+
}
122+
119123
const staleFamilies = new Set();
120124
const updatedFamilies = new Set();
121125

@@ -206,3 +210,7 @@ export function collectCustomHooksForSignature(type: any) {
206210
computeFullKey(signature);
207211
}
208212
}
213+
214+
export function getFamilyByID(id: string): Family | void {
215+
return allFamiliesByID.get(id);
216+
}

packages/react-fresh/src/__tests__/ReactFresh-test.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ let act;
2020
describe('ReactFresh', () => {
2121
let container;
2222
let lastRoot;
23+
let findHostNodesForHotUpdate;
2324
let scheduleHotUpdate;
2425

2526
beforeEach(() => {
2627
global.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
2728
supportsFiber: true,
2829
inject: injected => {
2930
scheduleHotUpdate = injected.scheduleHotUpdate;
31+
findHostNodesForHotUpdate = injected.findHostNodesForHotUpdate;
3032
},
3133
onCommitFiberRoot: (id, root) => {
3234
lastRoot = root;
@@ -2934,4 +2936,112 @@ describe('ReactFresh', () => {
29342936
expect(finalEl.textContent).toBe('1');
29352937
}
29362938
});
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+
}
29373047
});

0 commit comments

Comments
 (0)