Issue with curried HOC
See original GitHub issueHello!
Last December I opened an issue (#404) regarding a problem with HMR when Prefresh encounters out of date references, caused by circular dependencies.
I started reworking my code to stop having to rely on these circular dependencies in the first place, but encountered a second problem in doing so: it seems like Prefresh does not support currying for HOC.
Reproduction and investigation details
If we take the following code:
// Dummy curried HOC for reproduction purpose that does nothing interesting...
const withDisplayName = (displayName) => (Component) => {
Component.displayName = displayName
return () => <Component receivedDisplayName={displayName} />
}
const MyComponent = withDisplayName('MyCurriedHoc')((props) => {
return <div>{props.receivedDisplayName}</div>
})
If we look at the output of @prefresh/babel-plugin
, we see the following
const withDisplayName = (displayName) => (Component) => {
Component.displayName = displayName;
return () => /* @__PURE__ */ h(Component, {
receivedDisplayName: displayName
});
};
const MyComponent = withDisplayName("MyCurriedHoc")((props) => {
return /* @__PURE__ */ h("div", null, props.receivedDisplayName);
});
As we can see, the plugin did not save the reference to the component and does not register it for refresh. Interestingly enough in this precise situation, the first change made to the component… Correctly gets updated through HMR (using @web/dev-server). I don’t really know why this is happening since the if (import.meta.hot) {
bit is nowhere to be seen in the transformed code.
The second change however, triggers a full reload of the page (which is normal considering HMR handling has not been injected).
Next I tried storing the first currying stage in a temporary variable, as follows:
const withDisplayName = (displayName) => (Component) => {
Component.displayName = displayName
return () => <Component receivedDisplayName={displayName} />
}
const withCurriedName = withDisplayName('MyCurriedHoc')
const MyComponent = withCurriedName((props) => {
return <div>{props.receivedDisplayName}!!!</div>
})
With this change, the transformed output becomes:
const withDisplayName = displayName => Component => {
Component.displayName = displayName;
return () => /* @__PURE__ */h(Component, {
receivedDisplayName: displayName
});
};
const withCurriedName = withDisplayName("MyCurriedHoc");
const MyComponent = withCurriedName(_c = props => {
return /* @__PURE__ */h("div", null, props.receivedDisplayName);
});
_c2 = MyComponent;
export const StubbablePlayground = MyComponent;
var _c, _c2;
$RefreshReg$(_c, "MyComponent$withCurriedName");
$RefreshReg$(_c2, "MyComponent");
if (import.meta.hot) {
self.$RefreshSig$ = prevRefreshSig;
self.$RefreshReg$ = prevRefreshReg;
import.meta.hot.accept(() => {
try {
flushUpdates();
} catch(e) {
import.meta.hot.invalidate();
}
});
}
Now we can clearly see that HMR handling has been injected as expected, and with this everything works fine.
Is this a known limitation? It would really help not having to rely on a temporary variable to store the curried HOC before calling it.
I’ve looked into the code of @prefresh/babel-plugin
but I’m not knowledgeable enough in Babel to know where to add checks for this myself.
Issue Analytics
- State:
- Created 2 years ago
- Comments:10 (10 by maintainers)
Not really as there are multiple unnamed funcs in there 😅 I’ll try to take a closer look tonight
There you go 😃 Thanks for you help and let see if we can make this work 😃