-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Expand file tree
/
Copy pathcomponent.js
More file actions
131 lines (115 loc) · 4.6 KB
/
Copy pathcomponent.js
File metadata and controls
131 lines (115 loc) · 4.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import { assign } from './util';
import { diff, commitRoot } from './diff/index';
import options from './options';
import { Fragment } from './create-element';
/**
* Base Component class. Provides `setState()` and `forceUpdate()`, which
* trigger rendering
* @param {object} props The initial component props
* @param {object} context The initial context from parent components'
* getChildContext
*/
export function Component(props, context) {
this.props = props;
this.context = context;
// if (this.state==null) this.state = {};
// this.state = {};
// this._dirty = true;
// this._renderCallbacks = []; // Only class components
// Other properties that Component will have set later,
// shown here as commented out for quick reference
// this.base = null;
// this._ancestorComponent = null; // Always set right after instantiation
// this._vnode = null;
// this._nextState = null; // Only class components
// this._prevVNode = null;
// this._processingException = null; // Always read, set only when handling error
// this._constructor = null; // Only functional components, always set right after instantiation
}
/**
* Update component state and schedule a re-render.
* @param {object | ((s: object, p: object) => object)} update A hash of state
* properties to update with new values or a function that given the current
* state and props returns a new partial state
* @param {() => void} [callback] A function to be called once component state is
* updated
*/
Component.prototype.setState = function(update, callback) {
// only clone state when copying to nextState the first time.
let s = (this._nextState!==this.state && this._nextState) || (this._nextState = assign({}, this.state));
// if update() mutates state in-place, skip the copy:
if (typeof update!=='function' || (update = update(s, this.props))) {
assign(s, update);
}
// Skip update if updater function returned null
if (update==null) return;
if (callback) this._renderCallbacks.push(callback);
enqueueRender(this);
};
/**
* Immediately perform a synchronous re-render of the component
* @param {() => void} [callback] A function to be called after component is
* re-renderd
*/
Component.prototype.forceUpdate = function(callback) {
let vnode = this._vnode, dom = this._vnode._dom, parentDom = this._parentDom;
if (parentDom) {
// Set render mode so that we can differantiate where the render request
// is coming from. We need this because forceUpdate should never call
// shouldComponentUpdate
const force = callback!==false;
let mounts = [];
dom = diff(dom, parentDom, vnode, vnode, this._context, parentDom.ownerSVGElement!==undefined, null, mounts, this._ancestorComponent, force);
if (dom!=null && dom.parentNode!==parentDom) {
parentDom.appendChild(dom);
}
commitRoot(mounts, vnode);
}
if (callback) callback();
};
/**
* Accepts `props` and `state`, and returns a new Virtual DOM tree to build.
* Virtual DOM is generally constructed via [JSX](http://jasonformat.com/wtf-is-jsx).
* @param {object} props Props (eg: JSX attributes) received from parent
* element/component
* @param {object} state The component's current state
* @param {object} context Context object, as returned by the nearest
* ancestor's `getChildContext()`
* @returns {import('./index').ComponentChildren | void}
*/
Component.prototype.render = Fragment;
/**
* The render queue
* @type {Array<import('./internal').Component>}
*/
let q = [];
/**
* Asynchronously schedule a callback
* @type {(cb) => void}
*/
const defer = typeof Promise=='function' ? Promise.prototype.then.bind(Promise.resolve()) : setTimeout;
/*
* The value of `Component.debounce` must asynchronously invoke the passed in callback. It is
* important that contributors to Preact can consistenly reason about what calls to `setState`, etc.
* do, and when their effects will be applied. See the links below for some further reading on designing
* asynchronous APIs.
* * [Designing APIs for Asynchrony](https://blog.izs.me/2013/08/designing-apis-for-asynchrony)
* * [Callbacks synchronous and asynchronous](https://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/)
*/
/**
* Enqueue a rerender of a component
* @param {import('./internal').Component} c The component to rerender
*/
export function enqueueRender(c) {
if (!c._dirty && (c._dirty = true) && q.push(c) === 1) {
(options.debounceRendering || defer)(process);
}
}
/** Flush the render queue by rerendering all queued components */
function process() {
let p;
while ((p=q.pop())) {
// forceUpdate's callback argument is reused here to indicate a non-forced update.
if (p._dirty) p.forceUpdate(false);
}
}