- Start Date: 2018-10-28
- RFC PR: (leave this empty)
- React Issue: (leave this empty)
During the reconciliation ReactDOM compares "old" rendered element, and a new one. If type or key does not match - the old element will be removed, and new one will took it's place.
This makes stuff, like hot-reloading, a bit complex, and very flaky.
Everything could be solved by a dev-time ability to control tree reconciliation, via a "compare-control" hook inside React-DOM internals. "compare-control" hook will be able to control the moment type comparison, telling React that types are still the same, even if not equal by a reference, and let React-Hot-Loader rely on React internals, not to hack them.
// default behavior - pick A if A and B the same
React.compareControl( (typeA, typeB) => typeA === typeB ? typeA : null)
// threat any components with the same name as the same
React.compareControl( (typeA, typeB) => typeA.displayName === typeB.displayName)
This change would be able to solve some React-Hot-Loader issues:
- issue,
<Component />.type !== Component
, as long return type from createElement is a "Proxy". - issue, React-Hot-Loader is not hooks-friendly. The self-tree rendering is not working anymore.
- issue, multiple logical issues, we cannot solve in a "user space"
- (fact), React-Hot-Loader convert all SFC into Classes.
To prevent any future problem and enable a better HMR experience we need to change React. This logic would be needed not only for React-Hot-Loader, but for any other "hot-react-replacement", as least to mitigate side effects.
During Hot Module Replacement the type for a existing component changes, and this causes a whole tree remount.
React-Hot-Loader mitigates this by hacking into createElement
, and always providing
the same type for different component generations. The type is always not equal to
the original type, and represents "Proxy" type, which is always a Class-based component.
To track "similarity" React-Hot-Loader performs a register
operation, alering the code by babel plugin,
bounding type to a file and a local variable name of the type origin.
Unfortunately this operation could not handle types created in HOCs, or just not extracted as a top level variables.
To manage those "hidden" types React-Hot-Loader travers React-Tree by it'own, comparing existing tree, with the one it renders itself.
compare-control
hook is React alternative to this operation, idea was already proven
via integration to Preact, which, hopefully, was not yet merged.
compare-control
should return false, if types are different, and new one should replace the old.
compare-control
should return true, if types are equal, and new one should be rendered in the old place.
If type has as instance, created using a old type - it should be preserved.
Results(after disabling most of React-Hot-Loader logic):
- hooks supported out of the box.
- SFC are used directly, and not wrapped by Class components.
- No need of custom tree traversal and rendering.
- React-Hot-Loader would be able to perform "hot-patching" inside comparison function(like this)
Why should we not do this? Please consider:
- this is only for the React-Hot-Loader
- some libraries still need a custom tree traversal, and it will solve the same problem.
There is no other future-proof way
Not to be used by developers.
This is not a public feature
-
Ideally on the type change React should recreate an instance, with all the data preserved. This is quite dangerous, and could be better solved by hooks.
-
SFC are wrapped by Components for ability to forceUpdate them. How to update naked SFC, especially memo-ized.
-
It would be nice to have before rendering hook(componentWillUpdate), or have an access to instance, to apply instance-bound changes. Right now we have to wrap render method, and that's breaking Dev Tools