我一直在尝试React Hooks,它们确实简化了诸如存储状态之类的事情。但是,他们似乎通过魔术来做很多事情,我找不到关于它们实际工作方式的好文章。
看似神奇的第一件事是,每次调用setXXX方法时,调用useState()之类的函数如何导致功能组件的重新渲染?
当功能组件甚至没有能力在Mount / Unmount上运行代码时,诸如useEffect()之类的东西如何伪造componentDidMount?
useContext()如何真正获得对上下文的访问,甚至如何知道正在调用哪个组件?
这甚至还没有覆盖到所有已经出现的第三方钩子,例如useDataLoader,它允许您使用以下内容...
const { data, error, loading, retry } = useDataLoader(getData, id)
数据,错误,加载和重试如何在组件更改时重新呈现?
抱歉,有很多问题,但是我想大多数问题可以归结为一个问题,即:
钩子后面的函数实际上如何访问正在调用它的功能/无状态组件,以便它可以记住重新渲染之间的内容并使用新数据启动重新渲染?
答案 0 :(得分:7)
反应挂钩利用了组件的隐藏状态,它存储在fiber内,光纤是与组件实例相对应的实体(从广义上讲,因为功能组件不会将实例创建为类组件)。
它是React渲染器,它使钩子可以访问相应的上下文,状态等。顺便说一下,它是React渲染器,它调用组件函数。因此,它可以将组件实例与在组件函数内部调用的挂钩函数相关联。
此代码段说明了其工作原理:
let currentlyRenderedCompInstance;
const compStates = new Map(); // maps component instances to their states
const compInstances = new Map(); // maps component functions to instances
function useState(initialState) {
if (!compStates.has(currentlyRenderedCompInstance))
compStates.set(currentlyRenderedCompInstance, initialState);
return [
compStates.get(currentlyRenderedCompInstance) // state
val => compStates.set(currentlyRenderedCompInstance, val) // state setter
];
}
function render(comp, props) {
const compInstanceToken = Symbol('Renderer token for ' + comp.name);
if (!compInstances.has(comp))
compInstances.set(comp, new Set());
compInstances.get(comp).add(compInstanceToken);
currentlyRenderedCompInstance = compInstanceToken;
return {
instance: compInstanceToken,
children: comp(props)
};
}
类似于useState
通过currentlyRenderedCompInstance
访问当前呈现的组件实例令牌的方式,其他内置的钩子也可以执行此操作并维护该组件实例的状态。
答案 1 :(得分:3)
Dan Abramov在几天前创建了一篇博客文章,内容涉及以下内容:
https://overreacted.io/how-does-setstate-know-what-to-do/
第二部分专门介绍了有关useState之类的钩子的详细信息。
答案 2 :(得分:0)
我建议阅读https://eliav2.github.io/how-react-hooks-work/
它包含有关使用 React hook 时发生的事情的详细说明,并通过许多交互式示例进行演示。
注意 - 没有解释 React schduale 如何调用后期阶段,而是深入演示了 React 用于为后期阶段安排调用的规则是什么