/
react.ts
151 lines (126 loc) · 4.11 KB
/
react.ts
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import * as React from 'react'
import { isFunction, EMPTY_OBJ, ensure } from '@tarojs/shared'
import { Current } from '../current'
import { AppInstance, ReactPageInstance, ReactPageComponent, PageProps, Instance } from './instance'
import { document } from '../bom/document'
import { injectPageInstance, getPageInstance } from './common'
export function connectReactPage (
R: typeof React,
id: string
) {
const h = R.createElement
return (component: ReactPageComponent): React.FunctionComponent<PageProps> => {
const isReactComponent = !!component.prototype.isReactComponent ||
component.prototype instanceof R.Component // compat for some others react-like library
const inject = (node?: Instance) => node && injectPageInstance(node, id)
const refs = isReactComponent ? { ref: inject } : { forwardedRef: inject }
return (props: PageProps) => {
return h(
'root',
{ id },
h(PageContext.Provider, { value: id }, h(component, {
...props,
...refs
}))
)
}
}
}
export let PageContext: React.Context<string>
export const taroHooks = (lifecycle: string) => {
return (fn: Function) => {
const id = React.useContext(PageContext)
let inst = getPageInstance(id)
React.useLayoutEffect(() => {
let first = false
if (inst == null) {
first = true
inst = Object.create(null)
}
inst![lifecycle] = fn.bind(null)
if (first) {
injectPageInstance(inst!, id)
}
}, [])
}
}
export let react: typeof React
export function createReactApp (App: React.ComponentClass) {
// 初始值设置为 any 主要是为了过 TS 的校验
let R: typeof React = EMPTY_OBJ
let ReactDOM
if (process.env.FRAMEWORK === 'nerv') {
R = require('nervjs')
ReactDOM = R
}
// 其它 react-like 框架走 react 模式,在 webpack.resolve.alias 设置 react/react-dom 到对应包
if (process.env.FRAMEWORK === 'react') {
R = require('react')
ReactDOM = require('react-dom')
}
react = R
PageContext = R.createContext('')
ensure(!!ReactDOM, '构建 React/Nerv 项目请把 process.env.FRAMEWORK 设置为 \'react\'/\'nerv\' ')
const ref = R.createRef<ReactPageInstance>()
let wrapper: AppWrapper
class AppWrapper extends R.Component {
// run createElement() in a render function to make sure that owner is right
private pages: Array<() => React.FunctionComponentElement<PageProps>> = []
private elements: Array<React.FunctionComponentElement<PageProps>> = []
public mount (component: React.FunctionComponent<PageProps>, id: string, cb: () => void) {
const page = () => R.createElement(component, { key: id, tid: id })
this.pages.push(page)
this.forceUpdate(cb)
}
public unmount (id: string, cb: () => void) {
for (let i = 0; i < this.elements.length; i++) {
const element = this.elements[i]
if (element.key === id) {
this.elements.splice(i, 1)
break
}
}
this.forceUpdate(cb)
}
public render () {
while (this.pages.length > 0) {
const page = this.pages.pop()!
this.elements.push(page())
}
return R.createElement(
App,
{ ref },
this.elements.slice()
)
}
}
class AppConfig implements AppInstance {
onLaunch () {
wrapper = ReactDOM.render(R.createElement(AppWrapper), document.getElementById('app'))
}
onShow (options: unknown) {
const app = ref.current
if (app != null && isFunction(app.componentDidShow)) {
app.componentDidShow(options)
}
}
onHide (options: unknown) {
const app = ref.current
if (app != null && isFunction(app.componentDidHide)) {
app.componentDidHide(options)
}
}
render (cb: () => void) {
wrapper.forceUpdate(cb)
}
mount (component: ReactPageComponent, id: string, cb: () => void) {
const page = connectReactPage(R, id)(component)
wrapper.mount(page, id, cb)
}
unmount (id: string, cb: () => void) {
wrapper.unmount(id, cb)
}
}
Current.app = new AppConfig()
return Current.app
}