Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React Hook 源码阅读笔记 #35

Open
QC-L opened this issue Apr 16, 2019 · 0 comments
Open

React Hook 源码阅读笔记 #35

QC-L opened this issue Apr 16, 2019 · 0 comments

Comments

@QC-L
Copy link
Owner

QC-L commented Apr 16, 2019

Hook 推出之后增强了函数组件。让函数组件拥有了如 class 组件一般的特性,甚至超越 class 组件的存在。因此,通过阅读源码来看看 React Hook 的真面目。

我们会从以下几个文件来阅读 Hook 相关源码:

  • React.js
  • ReactHooks.js
  • ReactCurrentDispatcher.js
  • ReactFiberHooks.js

React.js

话不多说,让我们一探究竟!

首先,代码阅读先从入口开始:

React 引入方式如下:

import React from 'react';

如果使用了 Component,Hook 之类代码,引入方式如下所示:

import React, {
  Component,
  useState,
  useEffect
} from 'react';

由上述代码可以知,Hook 绑定在 React 对象之中

按照这条线索寻找,会在 react/src/React.js 文件中发现如下代码:

import {
  useCallback,
  useContext,
  useEffect,
  useImperativeHandle,
  useDebugValue,
  useLayoutEffect,
  useMemo,
  useReducer,
  useRef,
  useState,
} from './ReactHooks';

const React = {
  ... // more code
  useCallback,
  useContext,
  useEffect,
  useImperativeHandle,
  useDebugValue,
  useLayoutEffect,
  useMemo,
  useReducer,
  useRef,
  useState,
  ... // more code
};

export default React;

由此可知,Hook 相关的逻辑都保存在了 react/src/ReactHooks.js 当中。

ReactHooks.js

打开 react/src/ReactHooks.js 文件,先来看看引入的头文件:

import type {ReactContext} from 'shared/ReactTypes'; // 引入 ReactContext 类型 (给 flow 使用)
import invariant from 'shared/invariant';  // invariant 类似于断言,用于阻止代码执行
import warning from 'shared/warning'; // warning 用于抛出警告

import ReactCurrentDispatcher from './ReactCurrentDispatcher'; // 后面会对其进行讲解

从头文件中我们得出以下信息:

  1. 引入了 ReactContext 的类型,说明要给 useContext 作类型判断;
  2. 使用 invariant 作断言,说明某些代码执行不过,可能会中端;
  3. 使用 warning 抛出警告信息;
  4. ReactCurrentDispatcher 暂时不知其作用。

看完头文件,来看看 Hook 的相关实现:

// useState
export function useState<S>(initialState: (() => S) | S) {
  const dispatcher = resolveDispatcher();
  return dispatcher.useState(initialState);
}

... // more code

export function useEffect(
  create: () => (() => void) | void,
  inputs: Array<mixed> | void | null,
) {
  const dispatcher = resolveDispatcher();
  return dispatcher.useEffect(create, inputs);
}

... // more code

从以上代码可以得到几点信息:

  1. 代码中针对 useContext 部分,使用了 warning 抛出警告
  2. 都调用了 resolveDispatcher() 函数;
  3. resolveDispatcher() 会返回一个 dispatcher
  4. dispatcher 中实现了所有的 hook;
  5. 使用了 Flow(同其他源码)。

接下来,查看 resolveDispatcher() 函数:

function resolveDispatcher() {
  const dispatcher = ReactCurrentDispatcher.current;
  invariant(
    dispatcher !== null,
    'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
      ' one of the following reasons:\n' +
      '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
      '2. You might be breaking the Rules of Hooks\n' +
      '3. You might have more than one copy of React in the same app\n' +
      'See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.',
  );
  return dispatcher;
}

通过以上代码可知:

  1. dispatcher 实质上就是通过 ReactCurrentDispatcher.current 获取的;
  2. 在获取 dispatcher 后,使用 invariantdispatcher 进行了类似于断言的操作(用于进行规则和版本鉴别);

综上所述,我猜测 Hook 的核心文件可能是 ReactCurrentDispatcher.js

ReactCurrentDispatcher.js

然而,我错了。
此文件的代码如下:

import type {Dispatcher} from 'react-reconciler/src/ReactFiberHooks';

/**
 * Keeps track of the current dispatcher.
 */
const ReactCurrentDispatcher = {
  /**
   * @internal
   * @type {ReactComponent}
   */
  current: (null: null | Dispatcher),
};

export default ReactCurrentDispatcher;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant