59

我一直在尝试 React Hooks,它们似乎确实简化了诸如存储状态之类的事情。然而,他们似乎通过魔法做了很多事情,我找不到一篇关于他们实际工作的好文章。

看起来很神奇的第一件事是,每次调用它返回的 setXXX 方法时,调用 useState() 之类的函数如何导致重新渲染功能组件?

当功能组件甚至无法在 Mount/Unmount 上运行代码时,useEffect() 之类的东西是如何伪造 componentDidMount 的?

useContext() 如何实际访问上下文,它甚至如何知道哪个组件正在调用它?

这甚至还没有开始涵盖所有已经出现的第 3 方挂钩,例如 useDataLoader,它允许您使用以下内容......

const { data, error, loading, retry } = useDataLoader(getData, id)

当组件发生变化时,数据、错误、加载和重试如何重新渲染您的组件?

抱歉,有很多问题,但我想其中大部分都可以归结为一个问题,即:

钩子后面的函数如何真正访问调用它的功能/无状态组件,以便它可以记住重新渲染之间的内容并使用新数据启动重新渲染?

4

4 回答 4

52

React hook 利用了组件的隐藏状态,它存储在一个Fiber内部,一个 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,其他内置挂钩也可以执行此操作并维护此组件实例的状态。

于 2018-12-11T19:06:01.160 回答
14

几天前,丹·阿布拉莫夫(Dan Abramov)创建了一篇博客文章,其中涵盖了这一点:

https://overreacted.io/how-does-setstate-know-what-to-do/

后半部分专门介绍了有关 useState 等钩子的详细信息。

对于那些有兴趣深入了解一些实现细节的人,我在这里有一个相关的答案:React hooks 如何确定它们所针对的组件?

于 2018-12-11T19:13:24.983 回答
3

我建议阅读https://eliav2.github.io/how-react-hooks-work/

它包括有关使用反应钩子时发生的事情的详细解释,并通过许多交互式示例进行演示。

注意 - 这篇文章没有用技术术语解释 React 调度如何调用后期阶段,而是演示了 react 用于调度后期阶段调用的规则是什么。

于 2021-05-05T07:51:59.747 回答
1

Eliav Louski 在此评论上方给出的答案中的 URL(仅供参考 - https://eliav2.github.io/how-react-hooks-work/)是迄今为止我遇到的最好的 React 解释。这个页面应该取代 react 的官方教程,因为它消除了钩子和朋友的所有魔法......上帝保佑你 Eliav。你用那个博客让我的生活变得如此轻松。如果我在第一天看到这个博客,那将是一种祝福。

于 2022-02-26T06:45:31.223 回答