我一直在尝试React Hooks,它们似乎确实简化了诸如存储状态之类的事情。但是,他们似乎通过魔术来做很多事情,而我找不到关于它们实际工作方式的好文章。
看起来很神奇的第一件事是,每次调用setXXX方法时,调用诸如useState()之类的函数如何导致功能组件的重新渲染?
当功能组件甚至没有能力在Mount / Unmount上运行代码时,诸如useEffect()之类的东西如何伪造componentDidMount?
useContext()如何真正获得对上下文的访问,甚至如何知道正在调用哪个组件?
而且,这甚至还没有覆盖已经出现的所有第三方钩子,例如useDataLoader,它允许您使用以下内容…
const { data, error, loading, retry } = useDataLoader(getData, id)
数据,错误,加载和重试如何在组件更改时重新呈现?
抱歉,有很多问题,但是我想大多数问题可以归结为一个问题:
钩子后面的函数实际上如何访问正在调用它的功能/无状态组件,以便它可以记住重新渲染之间的内容并使用新数据启动重新渲染?
React钩子利用组件的隐藏状态,它存储在光纤内部,光纤是与组件实例相对应的实体(从广义上讲,因为功能组件不会将实例创建为类组件)。
它是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,其他内置的钩子也可以执行此操作并维护该组件实例的状态。
useState
currentlyRenderedCompInstance