Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React: accessing internal operation queue of React

Tags:

reactjs

React collects operations(like DOM operations such 'ADD','REPLACE','REMOVE' and more) so they could execute them in batch in one shot at the end of each render.

for example, setState call inside React component is scheduled to the end of the construction of this React tree by adding this operation to the operation queue. then react will go over this queue and will decide what changes need to be made to the DOM.

React will decide whether or not to call another render based on whether or not the operation queue is empty.

more info on this awesome tutorial which summarizes the basics of how React works internally.

I need access to this queue to decide whether or not the current render was the last for a very custom React component. (YES maybe I can avoid it but currently, this is my requirement)
the access must be from inside of this component.

my check will be called from the lastest useEffect which is after the render ends and the DOM was updated and is the latest lifecycle event, so if the operation queue is empty there will be no more renders for sure. (here nice article explains and demonstrates the order of hook calls)

couldn't find any public API, but a workaround would also be acceptable. (forking and editing React is not a workaround)

this src file is probably the main logic to this queue. and this is the type of the actual queue. however this is the source code and this queue is not exported in the built versions of react(neither development or production build)

so, is there a way to access the internal operation queue of React?

like image 669
Eliav Louski Avatar asked Mar 09 '26 22:03

Eliav Louski


1 Answers

EDIT - Warning

this is for educational purposes only - do not use it on production! this approach is not safe based on React core team member - I've already asked. it could be safe if you plan to use a fixed version of React without upgrading later.

####### END OF EDIT #######

SOLVED!

so after many many hours digging into React codebase I finally wrote a hook that tells if any update is currently scheduled.

Note: would work for function components only, and this hook is not well tested.

you can see some internal state of React by the undocumented __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED property. this prop holds ReactCurrentOwner which is basically a reference to the current component that is being constructed.

const currentOwner = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner?.current;

currentOwner is the current component that is being constructed. this prop is available during renders only(because in effects is after render no component is being currently constructed). but because another render can be triggered from state set from effects we should always call ower check from the latest effect

inside it .current.memoizedProps you will find a linked list of all hooks that were declared to this point on this component.

each hook holds a queue for holding scheduled updates, and inside it, there is a pending prop that tells if any update is currently scheduled for the next render.

we could run over this linked list to find out if an update is scheduled by any hook:

const wouldUpdate = (currentOwner) => {
  let newObj = currentOwner?.memoizedState;
  // go over the linked list of hooks and find out if there is any pending update
  while (newObj && 'next' in newObj) {
    newObj = newObj['next'];
    if (newObj?.queue?.pending) return true;
  }
  return false;
};

so to summer up we could build a custom hook to check if the current render is the latest scheduled render:

const wouldUpdate = (currentOwner) => {
  let newObj = currentOwner?.memoizedState;
  // go over the linked list of hooks and find out if there is any pending update
  while (newObj && 'next' in newObj) {
    newObj = newObj['next'];
    if (newObj?.queue?.pending) return true;
  }
  return false;
};

export const useDoesUpdateIsScheduled = () => {
  // @ts-ignore
  // hold the current owner ref so we could call it from effects
  const currentOwner = useRef(React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner?.current);
  return () => wouldUpdate(currentOwner.current);
};

so many hours for so little code... ;<

Usage:

const YourComponent = (props) => {
  //..
  // code, hooks ,logic, effects would be here
  //..

  // should be called from the last useEffect
  const wouldUpdate = useDoesUpdateIsScheduled();
  useEffect(() => {
    console.log(wouldUpdate());
  });

  return <div>... your jsx here ...</div>;
};

screenshot of test component on mount:

enter image description here

you can see that on the latest render our hook tells us there are no pending updates.

you can also call wouldUpdate from function body but take into account that updates can be scheduled from effects (means that calling it while rendering would not catch these updates)

the popular why-did-you-render also uses this undocumented __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED property to achieve it's goal.

THATS IT(actually it wasn't worth the hours, and it will probably break on various cases as this is not public API).

like image 58
Eliav Louski Avatar answered Mar 12 '26 11:03

Eliav Louski



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!