I Love ReactJS

Talk about the hidden egg function in React

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:

  1. <T>(instance: T) => void

  2. {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

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>;
}

concurrent sample address

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.

Exit mobile version