-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathRender.js
148 lines (120 loc) · 3.31 KB
/
Render.js
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
import React from 'react'
import PropTypes from 'prop-types';
export function withData(fetch, MaybeComponent) {
function bind(Component) {
class WithDataClass extends React.Component {
constructor(props) {
super(props)
}
getChildContext() {
return { buffer: this.context.buffer }
}
componentWillMount() {
if (!this.context.buffer.locked) {
this.context.buffer.push(
fetch(this.props)
)
}
}
render() {
return this.context.buffer.locked
? React.createElement(Component, this.props)
: null
}
}
WithDataClass.contextTypes = {
buffer: PropTypes.object.isRequired,
}
WithDataClass.childContextTypes = {
buffer: PropTypes.object.isRequired,
}
return WithDataClass
}
// works as a decorator or as a function
return MaybeComponent ? bind(MaybeComponent) : Component => bind(Component)
}
function usingDispatchBuffer(buffer, Component) {
class DispatchBufferClass extends React.Component {
constructor(props) {
super(props)
}
getChildContext() {
return { buffer }
}
render() {
return React.createElement(Component, this.props)
}
}
DispatchBufferClass.childContextTypes = {
buffer: PropTypes.object.isRequired,
}
return DispatchBufferClass
}
class DispatchBuffer {
constructor(renderStrategy) {
this.promisesBuffer = []
this.locked = false
this.renderStrategy = renderStrategy
}
push(v) {
this.promisesBuffer.push(v)
}
fill(Element) {
return this.renderStrategy(Element)
}
clear() {
this.promisesBuffer = []
}
flush(alt, Element) {
return Promise.all(this.promisesBuffer).then((data) => {
// fire off all the actions synchronously
data.forEach((f) => {
if (!f) return;
if (Array.isArray(f)) {
f.forEach(x => x())
} else {
f()
}
})
this.locked = true
return {
html: this.renderStrategy(Element),
state: alt.flush(),
element: Element,
}
}).catch((err) => {
return Promise.reject({
err,
state: alt.flush(),
element: Element,
})
})
}
}
function renderWithStrategy(strategy) {
return (alt, Component, props) => {
alt.trapAsync = true
// create a buffer and use context to pass it through to the components
const buffer = new DispatchBuffer((Node) => {
return React[strategy](Node)
})
const Container = usingDispatchBuffer(buffer, Component)
// cache the element
const Element = React.createElement(Container, props)
// render so we kick things off and get the props
buffer.fill(Element)
// flush out the results in the buffer synchronously setting the store
// state and returning the markup
return buffer.flush(alt, Element)
}
}
export function toDOM(Component, props, documentNode, shouldLock) {
const buffer = new DispatchBuffer()
buffer.locked = !!shouldLock
const Node = usingDispatchBuffer(buffer, Component)
const Element = React.createElement(Node, props)
buffer.clear()
return React.render(Element, documentNode)
}
export const toStaticMarkup = renderWithStrategy('renderToStaticMarkup')
export const toString = renderWithStrategy('renderToString')