Hello, everyone. I'm Carson.
The code volume of
React
can be said to be quite large. Is there a function in such a large library that is not mentioned in the document but actually exists ?
the answer is yes.
this article will introduce you to hidden egg features that are not mentioned in three documents.
ref cleanup
in the current React
, Ref
has two data structures:
-
<T>(instance: T) => void
-
{current: T}
for most requirements, we will use the second data structure. It is also the data structure returned by useRef
and createRef
.
the first data structure is mainly used for DOM
monitoring. For example, in the following example, the size of div
is reflected in the height
state:
function MeasureExample() {
const [height, setHeight] = useState(0);
const measuredRef = useCallback(node => {
if (node !== null) {
setHeight(node.getBoundingClientRect().height);
}
}, []);
return (
<div ref={measuredRef}>Hello Kasong</div>
);
}
but in the above example, the size change of DOM
cannot be reflected in real time to the height
state. To reflect real-time changes, you need to use native API
that monitors DOM
, such as
-
ResizeObserver , monitor
DOM
size change -
IntersectionObserver , monitor
DOM
visual area changes -
MutationObserver , monitor
DOM
tree changes
these API
are usually event-driven, which involves unbinding events when monitoring is not needed.
since event binding occurs in the ref
callback, naturally, unbinding events should also occur in the ref
callback. For example, modify the above example with ResizeObserver
:
function MeasureExample() {
const [entry, setEntry] = useState();
const measuredRef = useCallback((node) => {
const observer = new ResizeObserver(([entry]) => {
setEntry(entry)
})
observer.observe(node)
// unbind event
return () => {
observer.disconnect()
}
}, [])
return (
<div ref={measuredRef}>Hello Kasong</div>
);
}
in this scenario, we want the ref
of the function type to return a new function for unbinding events (similar to the return value of the useEffect
callback).
in fact, this question was raised in # issues 15176 in 1919. The changes in # pull 25686 at the end of last year have been merged into the React main
branch.
this feature change is not mentioned in the Ref
section of the current React
document. It may be online in some small version in the future.
Module Component
do you think the following function components can render hello :
function Child() {
return {
render() {
return "hello";
}
};
}
the answer is yes, see Module Component online example .
this is actually a form of component that has existed since ancient times, called Module Component
(that is, the function component returns an object with render
attribute).
when Module Component
is encountered, React
will convert the corresponding function component ( Child
component in the above example) into Class Component
. The subsequent update process is the same as Class Component
.
Module Component
is expected to be removed in a future version (see # pull 15145 ), so it is not mentioned in the documentation.
if it has any practical effect, it may bring a little confusion to your colleagues.
enable global concurrent updates
in v18
, only updates triggered by concurrency features (such as useTransiton
) are concurrent updates, and updates triggered in other cases are synchronous updates.
how can you turn on concurrent updates globally without using the concurrency feature? The answer is to add the following line of spell to the project:
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentBatchConfig.transition= {
Handsome guy:'Kasong'
};
for example, for the following example, rendering a time-consuming list (each Item
component render
takes time 5ms):
function App() {
return (
<ul className="App">
{Array.from({ length: 100 }).map((_, i) => (
<Item key={i} num={i}>
{i}
</Item>
))}
</ul>
);
}
function Item({ children }) {
const cur = performance.now();
while (performance.now() - cur < 5) {}
return <li>{children}</li>;
}
the rendering torch picture without spell is as follows. The whole update process is in a macro task, which takes time 513ms:
when the spell is added, the rendering torch diagram is as follows. The whole update process is sliced in time, and each slice is about 5ms:
Why does the spell work?
there is a variable _ _ SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
in React
and ReactDOM
, which is used to share data between different packages.
for example, all Hook
are derived from the React
package, but the concrete implementation of Hook
is in the ReactDOM
package. In order to share Hook
among them, you need a medium, which is _ _ SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
.
Similarly, updates related data needs to be shared between React
and ReactDOM
, including whether updates are concurrent.
when we assign React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentBatchConfig.transition
, React
assumes that the current update is concurrent.
in this way, concurrent updates can be turned on globally.
of course, I don't recommend that you change the data under _ _ SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
. After all, the name of this variable is bluffing.
Summary
above are the hidden egg functions in the three React
. In fact, besides them, there are many unexposed API
in React
, such as Offscreen Component
like Keep-Alive
in Vue
.
currently, if you want to experience Offscreen Component
, you can only experience it indirectly through Suspense
( Suspense
can switch between pending
and mount components using Offscreen Component
).
what other React
hiding features do you know? Welcome to discuss ~
in the comments section.