编辑 - 警告
这仅用于教育目的 - 不要在生产中使用它!基于 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
用于保存计划的更新,在它里面,有一个pending
props告诉当前是否为下一次渲染计划了任何更新。
我们可以遍历这个链表,看看是否有任何钩子安排了更新:
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)。