Skip to content

Commit e6d1c20

Browse files
committed
init commit🌈
0 parents  commit e6d1c20

File tree

9 files changed

+506
-0
lines changed

9 files changed

+506
-0
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Tiny React
2+
3+
Entirely empty here

example/index.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import Freact, { useState } from 'src'
2+
import FreactDOM from 'src/vnode'
3+
;(window as any).react = Freact
4+
5+
function plus(curValue, setValue) {
6+
setValue(!curValue)
7+
}
8+
9+
function App({ data, children }: any) {
10+
const [value, setValue] = useState(true)
11+
12+
return (
13+
<section id={data} className="clazz1">
14+
<h1>{value.toString()}</h1>
15+
{value ? <strong>yes</strong> : ''}
16+
<div className="wrap">
17+
<div className="button" onClick={_ => plus(value, setValue)}>
18+
toggle
19+
</div>
20+
</div>
21+
22+
{children}
23+
</section>
24+
)
25+
}
26+
27+
function Kid() {
28+
const [value, setValue] = useState(~~(Math.random() * 100))
29+
30+
function handleClick() {
31+
setValue(~~(Math.random() * 100))
32+
}
33+
34+
return <div onClick={handleClick} className="kid button">{value}</div>
35+
}
36+
37+
FreactDOM.render(
38+
<App data={1}>
39+
<Kid />
40+
</App>,
41+
document.getElementById('friday')
42+
)

package.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "@waynecz/tiny-react",
3+
"version": "0.0.1",
4+
"description": "Minimum implementation of React with hooks",
5+
"main": "dist/index",
6+
"scripts": {
7+
"test": "npm run test"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/waynecz/tiny-react.git"
12+
},
13+
"keywords": [
14+
"react"
15+
],
16+
"author": "waynecz <451578533@qq.com>",
17+
"license": "MIT",
18+
"bugs": {
19+
"url": "https://github.com/waynecz/tiny-react/issues"
20+
},
21+
"homepage": "https://github.com/waynecz/tiny-react#readme"
22+
}

rollup.config.js

Whitespace-only changes.

src/index.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { isClass } from './utils'
2+
import { patch } from './vnode'
3+
import { ReactVNODE, FunctionComponent } from './model'
4+
5+
// TODO: comment for every variable
6+
// TODO: make codes human-readable
7+
let __currentDispatcher = null
8+
9+
const memoizedStates = []
10+
const stateSetters = []
11+
let cursor = 0
12+
13+
interface memoCursor {
14+
value: number
15+
__inited: boolean
16+
}
17+
18+
export class Component {
19+
private memoStatesCursors: memoCursor[] = []
20+
private pointer = -1
21+
private renderFunc = null
22+
23+
public base = null
24+
public isReactComponent = {}
25+
26+
public componentDidMount = null
27+
28+
constructor(public props = {}) {}
29+
30+
public _render(func) {
31+
this.renderFunc = func
32+
const vnode = this.renderFunc(this.props)
33+
34+
// every time after finishing render, reset cursor to -1
35+
this.pointer = -1
36+
return vnode
37+
}
38+
39+
public useState(initialValue) {
40+
this.pointer++
41+
42+
if (!this.memoStatesCursors[this.pointer]) {
43+
const memoState = initialValue
44+
45+
memoizedStates.push(memoState)
46+
stateSetters.push(createStateSetter(cursor, this))
47+
48+
let currentCursor = { value: cursor, __inited: true }
49+
50+
this.memoStatesCursors.push(currentCursor)
51+
cursor++
52+
}
53+
54+
const memoCursor = this.memoStatesCursors[this.pointer]
55+
56+
const getter = memoizedStates[memoCursor.value]
57+
const setter = stateSetters[memoCursor.value]
58+
59+
return [getter, setter]
60+
}
61+
62+
public useEffect(callback) {
63+
this.componentDidMount = callback
64+
}
65+
}
66+
67+
export function setCurrentDispatcher(instance) {
68+
__currentDispatcher = instance
69+
}
70+
71+
function createStateSetter(cursor, instance) {
72+
return function(newValue) {
73+
memoizedStates[cursor] = newValue
74+
const { base, renderFunc } = instance
75+
setCurrentDispatcher(instance)
76+
const newVnode = instance._render(renderFunc)
77+
patch(base, newVnode)
78+
}
79+
}
80+
81+
export function createElement(
82+
type: string | FunctionComponent,
83+
props: { [key: string]: any } | null,
84+
...children: []
85+
): ReactVNODE {
86+
if (isClass(type)) {
87+
console.warn(`Doesn\'t support class Component: ${type.toString()}`)
88+
return
89+
} else {
90+
const VDOM = {
91+
type: type,
92+
props: props || {},
93+
children
94+
}
95+
96+
return VDOM
97+
}
98+
}
99+
100+
export function useState(initialValue) {
101+
return __currentDispatcher.useState(initialValue)
102+
}
103+
104+
export function useEffect(callback) {
105+
return __currentDispatcher.useEffect(callback)
106+
}
107+
108+
export default {
109+
createElement,
110+
Component,
111+
useState,
112+
useEffect
113+
}

src/model.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export interface ReactVNODE {
2+
type: string | ((...args: any[]) => ReactVNODE)
3+
props: {
4+
[key: string]: any
5+
}
6+
children: ReactVNODE[]
7+
key?: string | number
8+
9+
__isComponent?: boolean
10+
updater?: any
11+
}
12+
13+
export interface ReactElement extends HTMLElement {
14+
__react__?: any
15+
__key__?: any
16+
__listeners__?: Map<string, any>
17+
}
18+
19+
export interface FunctionComponent {
20+
(props: object, children: ReactVNODE[]): ReactVNODE
21+
}
22+
23+
export type Primitive = string | number | boolean | undefined | null

src/utils.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
export function isFunction(anything: any): boolean {
2+
return !isClass(anything) && typeof anything === 'function'
3+
}
4+
5+
export function isClass(anything: any): boolean {
6+
return (
7+
typeof anything === 'function' &&
8+
Function.prototype.toString.call(anything).startsWith('class')
9+
)
10+
}
11+
12+
export function isEventBinding(prop: string): boolean {
13+
return prop.startsWith('on')
14+
}
15+
16+
export function isStrOrNumber(anything: any): boolean {
17+
return typeof anything === 'string' || typeof anything === 'number'
18+
}
19+
20+
export function isNegitiveValue(anything: any): boolean {
21+
return anything === false || anything === null || anything === undefined
22+
}
23+
24+
export function isObject(anything: any): boolean {
25+
return Object.prototype.toString.call(anything) === '[object Object]'
26+
}
27+
28+
export function flatten<T>(array: T[]): T[] {
29+
return array.reduce((a, b) => {
30+
if (Array.isArray(b)) {
31+
return [...a, ...flatten(b)]
32+
} else {
33+
return [...a, b]
34+
}
35+
}, [])
36+
}
37+
38+
export function isEqual(object, other):Boolean {
39+
return object === other
40+
}

0 commit comments

Comments
 (0)