抹桥的博客

Seize the day.


  • 首页

  • 归档

  • 分类

  • 标签

  • 搜索

React 源码浅析之 - ReactBaseClasses

发表于 2017-09-20 更新于 2019-08-01 分类于 技术 阅读次数: Disqus:

引入的模块

var ReactNoopUpdateQueue = require('ReactNoopUpdateQueue');

var emptyObject = require('fbjs/lib/emptyObject');
var invariant = require('fbjs/lib/invariant');
var lowPriorityWarning = require('lowPriorityWarning');

其中, ReactNoopUpdateQueue 是默认的 updater ,用来提供 update, replaceState, setState 的入队操作,但可能是由于是默认 updater 的原因,只提供了 API 和对入参的校验,但没有提供实际的功能。比如:

enqueueSetState: function(
// 需要 render 的实例
publicInstance,
// 接下来要 merge 的 state
partialState,
// 可选参数,setState 组件 update 后的回调
callback,
// 可选参数,调用函数的的名字
callerName,
) {
warnNoop(publicInstance, 'setState');
},

其余几个模块是一些通用辅助模块,就不细说了。

Export 的对象

module.exports = {
Component: ReactComponent,
PureComponent: ReactPureComponent,
AsyncComponent: ReactAsyncComponent,
};

我们依次来看这几个 Component .

ReactComponent

/**
* Base class helpers for the updating state of a component.
*/
function ReactComponent(props, context, updater) {
this.props = props;
this.context = context;
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}
ReactComponent.prototype.isReactComponent = {};

用来创建基础组件的构造函数,其中 refs 默认是一个空对象,而 updater 默认就是我们上面说的 ReactNoopUpdateQueue.

setState

ReactComponent.prototype.setState = function(partialState, callback) {
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,
'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.',
);
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};

给它添加 setState 方法,实际操作就是把下一个要设置的 state 放入到更新队列里面。注释中提到:

  • 永远使用 setState 这个方法去更改 state ,你应该认为 this.state 是不可变的
  • 不保证 this.state 会及时更新,也就是说调用 this.state 可能拿到的还是旧的 state
  • 不保证 setState 会同步调用,有可能会把几个 setState 的调用一次性批量更新掉,一定要在某个 setState 的调用完成后执行-操作,可以提供可选的 callback 函数。
  • 当一个 callback 函数传入给 setState 的时候,它会在未来的某个时机调用。会用最新的组件参数 (state, props, context). 而这些参数和此时组件本身的 this.* 上的参数值可能会不一样,应为 callback 函数有可能在 receiveProps 之后 shouldComponentUpdate 之前调用,而此时这个新的 state, props 和 context 还没有赋值给 this.

其核心思想就是说, setState 的调用是异步的,从代码 this.updater.enqueueSetState(this, partialState, callback, 'setState'); 中可以看到,只是把要更新的 state 放入到了更新队列里面,而不是直接进行 state 的更新。

forceUpdate

ReactComponent.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};

一个强制更新的方法,这个方法不会触发 shoudleComponentUpdate ,但是会正常调用 componentWillUpdate 和 componentDidUpdate .
要确保调用这个方法的时候,所有的 DOM 事务操作已经完成。
只有在当你知道一个组件的深层次的 state 变化了,但是并没有调用 setState 的时候才需要调用这个方法。

不推荐的方法

/**
* Deprecated APIs. These APIs used to exist on classic React classes but since
* we would like to deprecate them, we're not going to move them over to this
* modern base class. Instead, we define a getter that warns if it's accessed.
*/
if (__DEV__) {
var deprecatedAPIs = {
isMounted: [
'isMounted',
'Instead, make sure to clean up subscriptions and pending requests in ' +
'componentWillUnmount to prevent memory leaks.',
],
replaceState: [
'replaceState',
'Refactor your code to use setState instead (see ' +
'https://github.com/facebook/react/issues/3236).',
],
};
var defineDeprecationWarning = function(methodName, info) {
Object.defineProperty(ReactComponent.prototype, methodName, {
get: function() {
lowPriorityWarning(
false,
'%s(...) is deprecated in plain JavaScript React classes. %s',
info[0],
info[1],
);
return undefined;
},
});
};
for (var fnName in deprecatedAPIs) {
if (deprecatedAPIs.hasOwnProperty(fnName)) {
defineDeprecationWarning(fnName, deprecatedAPIs[fnName]);
}
}
}

这里可以看到 isMounted 和 replaceState 这两个方法,已经被官方认为将要废弃的 API ,在开发过程中应该尽量避免使用它们。

PureComponent

function ReactPureComponent(props, context, updater) {
// Duplicated from ReactComponent.
this.props = props;
this.context = context;
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}

function ComponentDummy() {}
ComponentDummy.prototype = ReactComponent.prototype;
var pureComponentPrototype = (ReactPureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = ReactPureComponent;
// Avoid an extra prototype jump for these methods.
Object.assign(pureComponentPrototype, ReactComponent.prototype);
pureComponentPrototype.isPureReactComponent = true;

一个典型 js 实现继承的方式,可以看到 PureComponent 继承自 ReactComponent ,PureComponent 拥有 ReactComponent 的所有属性和方法,并在此基础上多了一个属性:

pureComponentPrototype.isPureReactComponent = true;

后面在组件更新部分会用到这个属性。

AsyncComponent

这个组件在我实际开发过程中还没有用到过,我们来看一下:

function ReactAsyncComponent(props, context, updater) {
// Duplicated from ReactComponent.
this.props = props;
this.context = context;
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}

var asyncComponentPrototype = (ReactAsyncComponent.prototype = new ComponentDummy());
asyncComponentPrototype.constructor = ReactAsyncComponent;
// Avoid an extra prototype jump for these methods.
Object.assign(asyncComponentPrototype, ReactComponent.prototype);
asyncComponentPrototype.unstable_isAsyncReactComponent = true;
asyncComponentPrototype.render = function() {
return this.props.children;
};

和 PureComponent 一样,同样继承自 ReactComponent. 再次基础上添加了
unstable_isAsyncReactComponent 属性,并设置了 render 方法为直接返回它的 children. 从新添加的属性名可以看到,这是一个还没有稳定下来的特性组件。从名字和内容初步判断是用来创建异步的组件,先用一个 React 组件占位,当 props 传入 children 的时候,在进行渲染,从 render 方法直接 return 可以猜到这样可以实现无多余嵌套 html 标签的组件。

总结

这个模块定义了 React 中会用到的三个基础组件类,需要注意的是:

1. State 应该被看作是 immutable 的,所有的对 state 的修改只能通过 setState 来完成,不能使用 `this.state = **` 来进行 state 的更新
2. setState 是一个异步的操作,所以在调用 setState 后从 this.state 上直接取值可能不是你期望的结果,读取的结果有可能依旧是老的 state

而组件内部的 updater 逻辑,这个模块里面并没有定义具体的执行逻辑。比如调用 setState 后会对要更新的 state 进行入队操作,那么这个操作过程中发生了什么,究竟是如何进行批量的更新的,以及什么时候 state 才会更新完毕,那就需要从其他的模块里来寻找答案了。

相关文章

  • React 源码浅析之 - 入口文件
  • React 源码浅析之 - ReactBaseClasses
  • React 源码浅析之 - ReactChildren
  • React 源码浅析之 - ReactElement
  • React 源码浅析之 - onlyChildren
如果觉得此文章对你有帮助,可以请我喝咖啡O(∩_∩)O
抹桥 微信支付

微信支付

抹桥 支付宝

支付宝

  • 本文作者: 抹桥
  • 本文链接: https://blog.kisnows.com/2017/09/20/react-source-code-analyze-2/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-ND 许可协议。转载请注明出处!
# React # source code
React 源码浅析之 - 入口文件
React 源码浅析之 - ReactChildren
  • 文章目录
  • 站点概览
抹桥

抹桥

怕什么真理无穷
67 日志
2 分类
54 标签
RSS
GitHub E-Mail Twitter Weibo
Creative Commons
  1. 1. 引入的模块
  2. 2. Export 的对象
    1. 2.1. ReactComponent
      1. 2.1.1. setState
      2. 2.1.2. forceUpdate
      3. 2.1.3. 不推荐的方法
    2. 2.2. PureComponent
    3. 2.3. AsyncComponent
  3. 3. 总结
  4. 4. 相关文章
© 2015 – 2019 抹桥
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Gemini v7.3.0