React:访问 React 内部操作队列

IT技术 reactjs
2021-05-24 14:10:32

React收集操作(如 DOM 操作,如“ADD”、“REPLACE”、“REMOVE”等),因此它们可以在每次渲染结束时一次性批量执行它们。

例如,React 组件内部的 setState 调用通过将此操作添加到操作队列来调度到此 React 树的构建结束。然后 react 将遍历这个队列并决​​定需要对 DOM 进行哪些更改。

React 会根据操作队列是否为空来决定是否调用另一个渲染。

关于这个很棒的教程的更多信息,它总结了 React 内部如何工作的基础知识。

我需要访问这个队列来决定当前渲染是否是一个非常自定义的 React 组件的最后一个渲染。(是的,也许我可以避免它,但目前,这是我的要求)
必须从该组件内部进行访问。

我的检查将从渲染结束和 DOM 更新后的最新 useEffect 调用,并且是最新的生命周期事件,因此如果操作队列为空,则肯定不会再进行渲染。这里不错的文章解释并演示了钩子调用的顺序)

找不到任何公共 API,但解决方法也是可以接受的。(分叉和编辑 React 不是解决方法)

这个src 文件可能是这个队列的主要逻辑。并且是实际队列的类型。然而,这是源代码,这个队列没有在 react 的构建版本中导出(无论是开发版本还是生产版本)

那么,有没有办法访问React内部的操作队列呢?

1个回答

编辑 - 警告

这仅用于教育目的 - 不要在生产中使用它!基于 React 核心团队成员, 这种方法并不安全- 我已经问过。如果您计划使用固定版本的 React 而稍后不升级,这可能是安全的。

####### 编辑结束#######

解决了!

因此,在深入研究 React 代码库数小时后,我终于编写了一个钩子,告诉您当前是否安排了任何更新。

注意:仅适用于功能组件,并且此钩子没有经过很好的测试。

您可以通过未记录的__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED属性查看 React 的一些内部状态这个 prop 持有ReactCurrentOwner它基本上是对正在构造的当前组件的引用。

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

currentOwner是正在构建的当前组件。此props仅在渲染期间可用(因为效果是在渲染之后当前没有构建组件)。但是因为可以从效果的状态集触发另一个渲染,所以我们应该总是从最新的效果中调用 ower 检查

在它里面,.current.memoizedProps你会找到一个所有钩子的链接列表,这些钩子在这个组件上声明到这一点。

每个钩子都有一个queue用于保存计划的更新,在它里面,有一个pendingprops告诉当前是否为下一次渲染计划了任何更新。

我们可以遍历这个链表,看看是否有任何钩子安排了更新:

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

所以到了夏天,我们可以构建一个自定义钩子来检查当前渲染是否是最新的预定渲染:

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

这么少的代码花了这么多小时......;<

用法:

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

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

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

挂载测试组件截图:

在此处输入图片说明

你可以看到在最新的渲染中,我们的钩子告诉我们没有挂起的更新。

您也可以wouldUpdate从函数体调用,但要考虑到可以从效果中安排更新(意味着在渲染时调用它不会捕获这些更新)

流行的为什么你渲染也使用这个未记录的__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED属性来实现它的目标。

这就是它(实际上它不值得花几个小时,并且它可能会在各种情况下中断,因为这不是公共 API)。