Skip to content

Commit 9b94390

Browse files
committed
Support hooks
Related prs + facebook/react#13968 + facebook/react#14569
1 parent 14c508f commit 9b94390

11 files changed

+465
-186
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ The dead simple implementation of React for the learning purpose on how React wo
99
- [x] Global event handler
1010
- [x] Fiber
1111
- [x] Priority
12-
- [ ] Hooks
12+
- [x] Hooks
1313
- [ ] Suspend
14-
- [ ] Synthetic event
14+
- [ ] Synthetic event

demo/app.js

Lines changed: 12 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,19 @@
11
import * as React from "../qreact";
22

3-
class App extends React.Component {
4-
constructor(props) {
5-
super(props);
6-
7-
this.state = {
8-
stories: [
9-
{
10-
id: 1,
11-
name: "[Webpack] — Smart Loading Assets For Production",
12-
url:
13-
"https://hackernoon.com/webpack-smart-loading-assets-for-production-3571e0a29c2e",
14-
},
15-
{
16-
id: 2,
17-
name: "V8 Engine Overview",
18-
url: "https://medium.com/@MQuy90/v8-engine-overview-7c965731ced4",
19-
},
20-
],
21-
};
22-
}
23-
24-
render() {
25-
const { stories } = this.state;
26-
27-
return (
28-
<div>
29-
<ul>
30-
{stories.map(story => (
31-
<Story story={story} key={story.id} onRemove={this.removeStory} />
32-
))}
33-
</ul>
34-
</div>
35-
);
36-
}
37-
38-
removeStory = story => () => {
39-
const { stories } = this.state;
40-
41-
const index = stories.findIndex(s => s.id == story.id);
42-
stories.splice(index, 1);
43-
44-
this.setState(stories);
45-
};
46-
}
47-
48-
class Story extends React.Component {
49-
constructor(props) {
50-
super(props);
51-
52-
this.state = { likes: Math.ceil(Math.random() * 100) };
53-
}
54-
render() {
55-
const { story, onRemove } = this.props;
56-
const { likes } = this.state;
57-
58-
return (
59-
<li>
60-
<button onClick={this.handleClick}>{likes}❤️</button>
61-
<a href={story.url}>{story.name}</a>
62-
<button onClick={onRemove(story)}>Remove</button>
63-
</li>
64-
);
65-
}
66-
67-
handleClick = () => {
68-
this.setState({
69-
likes: this.state.likes + 1,
70-
});
71-
};
3+
function Example() {
4+
const [count, setCount] = React.useState(0);
5+
6+
return (
7+
<div>
8+
<p>You clicked {count} times</p>
9+
<button onClick={() => setCount(count + 1)}>Click me</button>
10+
</div>
11+
);
7212
}
7313

7414
React.render(
75-
<React.unstable_AsyncMode>
76-
<App />
77-
</React.unstable_AsyncMode>,
15+
<div>
16+
<Example />
17+
</div>,
7818
document.getElementById("root"),
7919
);

demo/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"webpack-dev-server": "3.1.14"
2020
},
2121
"dependencies": {
22-
"react": "16.3.0",
23-
"react-dom": "16.3.3"
22+
"react": "16.8.0-alpha.1",
23+
"react-dom": "16.8.0-alpha.1"
2424
}
2525
}

demo/yarn.lock

Lines changed: 24 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -949,11 +949,6 @@ array-unique@^0.3.2:
949949
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
950950
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
951951

952-
asap@~2.0.3:
953-
version "2.0.6"
954-
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
955-
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
956-
957952
asn1.js@^4.0.0:
958953
version "4.9.2"
959954
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.2.tgz#8117ef4f7ed87cd8f89044b5bff97ac243a16c9a"
@@ -1459,11 +1454,6 @@ copy-descriptor@^0.1.0:
14591454
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
14601455
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
14611456

1462-
core-js@^1.0.0:
1463-
version "1.2.7"
1464-
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
1465-
integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
1466-
14671457
core-util-is@~1.0.0:
14681458
version "1.0.2"
14691459
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@@ -1804,13 +1794,6 @@ encodeurl@~1.0.2:
18041794
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
18051795
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
18061796

1807-
encoding@^0.1.11:
1808-
version "0.1.12"
1809-
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
1810-
integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=
1811-
dependencies:
1812-
iconv-lite "~0.4.13"
1813-
18141797
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
18151798
version "1.4.1"
18161799
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
@@ -2060,19 +2043,6 @@ faye-websocket@~0.11.1:
20602043
dependencies:
20612044
websocket-driver ">=0.5.1"
20622045

2063-
fbjs@^0.8.16:
2064-
version "0.8.17"
2065-
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
2066-
integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
2067-
dependencies:
2068-
core-js "^1.0.0"
2069-
isomorphic-fetch "^2.1.1"
2070-
loose-envify "^1.0.0"
2071-
object-assign "^4.1.0"
2072-
promise "^7.1.1"
2073-
setimmediate "^1.0.5"
2074-
ua-parser-js "^0.7.18"
2075-
20762046
figgy-pudding@^3.5.1:
20772047
version "3.5.1"
20782048
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
@@ -2520,7 +2490,7 @@ iconv-lite@0.4.23:
25202490
dependencies:
25212491
safer-buffer ">= 2.1.2 < 3"
25222492

2523-
iconv-lite@^0.4.4, iconv-lite@~0.4.13:
2493+
iconv-lite@^0.4.4:
25242494
version "0.4.24"
25252495
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
25262496
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
@@ -2770,7 +2740,7 @@ is-regex@^1.0.4:
27702740
dependencies:
27712741
has "^1.0.1"
27722742

2773-
is-stream@^1.0.1, is-stream@^1.1.0:
2743+
is-stream@^1.1.0:
27742744
version "1.1.0"
27752745
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
27762746
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
@@ -2817,14 +2787,6 @@ isobject@^3.0.0, isobject@^3.0.1:
28172787
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
28182788
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
28192789

2820-
isomorphic-fetch@^2.1.1:
2821-
version "2.2.1"
2822-
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
2823-
integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=
2824-
dependencies:
2825-
node-fetch "^1.0.1"
2826-
whatwg-fetch ">=0.10.0"
2827-
28282790
js-levenshtein@^1.1.3:
28292791
version "1.1.6"
28302792
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
@@ -3254,14 +3216,6 @@ no-case@^2.2.0:
32543216
dependencies:
32553217
lower-case "^1.1.1"
32563218

3257-
node-fetch@^1.0.1:
3258-
version "1.7.3"
3259-
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
3260-
integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==
3261-
dependencies:
3262-
encoding "^0.1.11"
3263-
is-stream "^1.0.1"
3264-
32653219
node-forge@0.7.5:
32663220
version "0.7.5"
32673221
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df"
@@ -3681,14 +3635,7 @@ promise-inflight@^1.0.1:
36813635
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
36823636
integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
36833637

3684-
promise@^7.1.1:
3685-
version "7.3.1"
3686-
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
3687-
integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
3688-
dependencies:
3689-
asap "~2.0.3"
3690-
3691-
prop-types@^15.6.0:
3638+
prop-types@^15.6.2:
36923639
version "15.6.2"
36933640
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
36943641
integrity sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==
@@ -3812,25 +3759,25 @@ rc@^1.2.7:
38123759
minimist "^1.2.0"
38133760
strip-json-comments "~2.0.1"
38143761

3815-
react-dom@16.3.3:
3816-
version "16.3.3"
3817-
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.3.tgz#af4c2aef9f6a66251a46da50253c860a67ae66d9"
3818-
integrity sha512-ALCp7ZbSGkqRDtQoZozKVNgwXMxbxf/IGOUMC2A0yF6JHeZrS8e2cOotPT87Vf4b7PKCuUVKU4/RDEXxToA/yA==
3762+
react-dom@16.8.0-alpha.1:
3763+
version "16.8.0-alpha.1"
3764+
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.0-alpha.1.tgz#dab73b8354ba2e498e3127d18e29d4546cea889e"
3765+
integrity sha512-tZCUM8BpnwUHJmLnUWP9c3vVZxnCqYotj7s4tx7umojG6BKv745KIBtuPTzt0EI0q50GMLEpmT/CPQ8iA61TwQ==
38193766
dependencies:
3820-
fbjs "^0.8.16"
38213767
loose-envify "^1.1.0"
38223768
object-assign "^4.1.1"
3823-
prop-types "^15.6.0"
3769+
prop-types "^15.6.2"
3770+
scheduler "^0.13.0-alpha.1"
38243771

3825-
react@16.3.0:
3826-
version "16.3.0"
3827-
resolved "https://registry.yarnpkg.com/react/-/react-16.3.0.tgz#fc5a01c68f91e9b38e92cf83f7b795ebdca8ddff"
3828-
integrity sha512-Qh35tNbwY8SLFELkN3PCLO16EARV+lgcmNkQnoZXfzAF1ASRpeucZYUwBlBzsRAzTb7KyfBaLQ4/K/DLC6MYeA==
3772+
react@16.8.0-alpha.1:
3773+
version "16.8.0-alpha.1"
3774+
resolved "https://registry.yarnpkg.com/react/-/react-16.8.0-alpha.1.tgz#c2b32689f3b466d3ce85a634dd9035f789d2cd97"
3775+
integrity sha512-vLwwnhM2dXrCsiQmcSxF2UdZVV5xsiXjK5Yetmy8dVqngJhQ3aw3YJhZN/YmyonxwdimH40wVqFQfsl4gSu2RA==
38293776
dependencies:
3830-
fbjs "^0.8.16"
38313777
loose-envify "^1.1.0"
38323778
object-assign "^4.1.1"
3833-
prop-types "^15.6.0"
3779+
prop-types "^15.6.2"
3780+
scheduler "^0.13.0-alpha.1"
38343781

38353782
"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@~2.3.6:
38363783
version "2.3.6"
@@ -4070,6 +4017,14 @@ sax@^1.2.4:
40704017
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
40714018
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
40724019

4020+
scheduler@^0.13.0-alpha.1:
4021+
version "0.13.0-alpha.1"
4022+
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.0-alpha.1.tgz#753977fb4fb35d8cdd559868a11e46b640955556"
4023+
integrity sha512-W0sH0848sVuPKg+I18vTYQyzVtA4X1lrVgSeXK6KnOPUltFdJcY5nkbTkjGUeS/E0x+eBsNYfSdhJtGjT95njw==
4024+
dependencies:
4025+
loose-envify "^1.1.0"
4026+
object-assign "^4.1.1"
4027+
40734028
schema-utils@^0.4.4:
40744029
version "0.4.7"
40754030
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"
@@ -4183,7 +4138,7 @@ set-value@^2.0.0:
41834138
is-plain-object "^2.0.3"
41844139
split-string "^3.0.1"
41854140

4186-
setimmediate@^1.0.4, setimmediate@^1.0.5:
4141+
setimmediate@^1.0.4:
41874142
version "1.0.5"
41884143
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
41894144
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
@@ -4583,11 +4538,6 @@ typedarray@^0.0.6:
45834538
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
45844539
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
45854540

4586-
ua-parser-js@^0.7.18:
4587-
version "0.7.19"
4588-
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b"
4589-
integrity sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==
4590-
45914541
uglify-js@3.4.x:
45924542
version "3.4.9"
45934543
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"
@@ -4887,11 +4837,6 @@ websocket-extensions@>=0.1.1:
48874837
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
48884838
integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==
48894839

4890-
whatwg-fetch@>=0.10.0:
4891-
version "3.0.0"
4892-
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
4893-
integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==
4894-
48954840
which-module@^2.0.0:
48964841
version "2.0.0"
48974842
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"

qreact.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import { render } from "./src/render";
22
import { createElement, REACT_ASYNC_MODE_TYPE } from "./src/createElement";
33
import { Component } from "./src/Component";
44
import { deferredUpdates } from "./src/FiberScheduler";
5+
import { useState } from "./src/FiberHooks";
56

67
const unstable_AsyncMode = REACT_ASYNC_MODE_TYPE;
78

89
export {
910
render,
1011
createElement,
1112
Component,
13+
useState,
1214
deferredUpdates,
1315
unstable_AsyncMode,
1416
};

src/Component.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ export class Component {
1414
insertUpdateIntoFiber(fiber, update);
1515
scheduleWork(fiber, expirationTime);
1616
}
17+
18+
isReactComponent = {};
1719
}
1820

1921
export const ReactInstanceMap = {

src/Fiber.js

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
HostText,
66
ClassComponent,
77
Mode,
8+
FunctionalComponent,
89
} from "./TypeOfWork";
910
import { REACT_ASYNC_MODE_TYPE, REACT_STRICT_MODE_TYPE } from "./createElement";
1011
import { AsyncMode, StrictMode } from "./TypeOfMode";
@@ -95,7 +96,11 @@ export function createFiberFromElement(element, mode, expirationTime) {
9596

9697
let fiberTag;
9798
if (typeof type === "function") {
98-
fiberTag = ClassComponent;
99+
if (shouldConstruct(type)) {
100+
fiberTag = ClassComponent;
101+
} else {
102+
fiberTag = FunctionalComponent;
103+
}
99104
} else if (typeof type === "string") {
100105
fiberTag = HostComponent;
101106
} else {
@@ -109,7 +114,7 @@ export function createFiberFromElement(element, mode, expirationTime) {
109114
mode |= StrictMode;
110115
break;
111116
default: {
112-
if (typeof type === "object" && type !== null) {
117+
if (typeof type === "object" && type != null) {
113118
if (typeof type.tag === "number") {
114119
// Currently assumed to be a continuation and therefore is a
115120
// fiber already.
@@ -144,20 +149,7 @@ export function createFiberFromText(content, mode, expirationTime) {
144149
return fiber;
145150
}
146151

147-
function createFiberFromElementType(type, key) {
148-
let fiber;
149-
if (typeof type === "function") {
150-
fiber = new FiberNode(ClassComponent, key);
151-
fiber.type = type;
152-
} else if (typeof type === "string") {
153-
fiber = new FiberNode(HostComponent, key);
154-
fiber.type = type;
155-
} else if (
156-
typeof type === "object" &&
157-
type != null &&
158-
typeof type.tag === "number"
159-
) {
160-
fiber = type;
161-
}
162-
return fiber;
152+
function shouldConstruct(Component) {
153+
const prototype = Component.prototype;
154+
return !!(prototype && prototype.isReactComponent);
163155
}

0 commit comments

Comments
 (0)