Skip to content

Commit efc76ce

Browse files
feat(overmind-components): properly handle update of children
1 parent dd0cefa commit efc76ce

File tree

37 files changed

+870
-885
lines changed

37 files changed

+870
-885
lines changed

packages/node_modules/overmind-components/src/component.ts

Lines changed: 104 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -6,77 +6,74 @@ import { getApp, getName, patch } from './utils'
66

77
const SELF = 'self'
88

9-
function copyVNode(source: VNode, target: VNode): void {
10-
target.elm = source.elm
11-
12-
target.data = source.data
13-
target.children = source.children
14-
target.text = source.text
15-
target.elm = source.elm
16-
}
17-
189
let nextId = 0
1910
let nextComponentId = {}
2011
let nextComponentInstanceId = {}
2112

22-
function init(componentVNode: VNode): void {
13+
function copyToComponent(vnode: VNode, thunk: VNode, component) {
14+
;(vnode.data as VNodeData).fn = (thunk.data as VNodeData).fn
15+
thunk.children = vnode.children
16+
thunk.text = vnode.text
17+
thunk.elm = vnode.elm
18+
19+
thunk.data = {
20+
...vnode.data,
21+
hook: {
22+
...(vnode.data as any).hook,
23+
prepatch,
24+
postpatch,
25+
destroy,
26+
},
27+
component,
28+
}
29+
30+
component.currentNode = thunk
31+
}
32+
33+
function init(componentNode: VNode): void {
2334
try {
24-
const component = (componentVNode.data as VNodeData).component
35+
const component = (componentNode.data as VNodeData).component
2536
const name = component.name
26-
const tree = component.tree
27-
let currentNode = componentVNode
28-
29-
const createComponentVNode = (newState) => {
30-
return (component.fn as any)(component.props, {
31-
changeState: update,
32-
state:
33-
typeof newState === 'undefined'
34-
? (currentNode.data as VNodeData).component.state
35-
: newState,
36-
store: tree.state,
37-
actions: getApp().actions,
37+
const tree = (component.tree = getApp().proxyStateTree.getTrackStateTree())
38+
39+
component.currentNode = componentNode
40+
41+
const update = (flushId?) => {
42+
patch(component.currentNode, {
43+
...component.currentNode,
44+
flushId: flushId,
3845
})
3946
}
4047

41-
const update = (newState?) => {
42-
tree.track(onFlush)
43-
try {
44-
currentNode.elm = componentVNode.elm
45-
const newNode = createComponentVNode(newState)
46-
newNode.elm = componentVNode.elm
47-
currentNode = patch(currentNode, newNode)
48-
currentNode.elm = componentVNode.elm
49-
} catch (error) {
50-
console.log(name, error.message, currentNode)
51-
}
52-
;(currentNode.data as any).component = (componentVNode.data as any).component
53-
copyVNode(currentNode, componentVNode)
54-
tree.stopTracking()
55-
}
48+
const createSelfNode = () => {
49+
const node = (component.fn as any)(component.props, {
50+
changeState: (newState) => {
51+
component.state = newState
52+
update()
53+
},
54+
state: component.state,
55+
appState: tree.state,
56+
actions: getApp().actions,
57+
reaction: getApp().reaction,
58+
})
5659

57-
const onFlush = (_, __, flushId) => {
58-
update()
60+
if (node.sel !== SELF) {
61+
throw new Error(
62+
`You are not returning a <self /> element from the component ${name} `
63+
)
64+
}
5965

60-
getApp().eventHub.emitAsync(EventType.COMPONENT_UPDATE, {
61-
componentId: (currentNode.data as any).component.componentId,
62-
componentInstanceId: (currentNode.data as any).component
63-
.componentInstanceId,
64-
name,
65-
flushId,
66-
paths: Array.from(tree.pathDependencies) as any,
67-
})
66+
return node
6867
}
69-
tree.track(onFlush)
70-
currentNode = createComponentVNode(undefined)
71-
;(currentNode.data as any).component = (componentVNode.data as any).component
72-
73-
if (currentNode.sel !== SELF) {
74-
throw new Error(
75-
`You are not returning a <self /> element from the component ${name} `
76-
)
68+
component.onFlush = (_, __, flushId) => {
69+
component.currentFlushId = flushId
70+
update(flushId)
7771
}
72+
tree.track(component.onFlush)
73+
const newNode = createSelfNode()
7874
tree.stopTracking()
79-
copyVNode(currentNode, componentVNode)
75+
76+
copyToComponent(newNode, componentNode, component)
8077

8178
if (!(name in nextComponentId)) {
8279
nextComponentId[name] = nextId++
@@ -107,54 +104,71 @@ function init(componentVNode: VNode): void {
107104
}
108105

109106
function prepatch(oldVnode: VNode, vnode: VNode): void {
110-
try {
111-
const oldComponent = (oldVnode.data as VNodeData).component
112-
const newComponent = (vnode.data as VNodeData).component
113-
const oldProps = oldComponent.props || {}
114-
const props = newComponent.props || {}
115-
const oldPropsKeys = Object.keys(oldProps)
116-
const propsKeys = Object.keys(props)
117-
const overmind = {
118-
changeState: newComponent.changeState,
119-
state: newComponent.state,
120-
store: newComponent.tree.state,
121-
actions: getApp().actions,
122-
}
107+
const oldComponent = (oldVnode.data as VNodeData).component || {}
108+
const newComponent = (vnode.data as VNodeData).component || {}
109+
const oldProps = oldComponent.props || {}
110+
const props = newComponent.props || {}
111+
const oldPropsKeys = Object.keys(oldProps)
112+
const propsKeys = Object.keys(props)
113+
const overmind = {
114+
changeState: oldComponent.changeState,
115+
state: oldComponent.state,
116+
appState: oldComponent.tree.state,
117+
actions: getApp().actions,
118+
reaction: getApp().reaction,
119+
}
123120

124-
if (
125-
oldComponent.fn !== newComponent.fn ||
126-
oldPropsKeys.length !== propsKeys.length
127-
) {
128-
copyVNode((newComponent.fn as any)(props, overmind), vnode)
129-
return
130-
}
121+
oldVnode.children = oldComponent.currentNode.children
131122

132-
const keys = new Set<string>([...oldPropsKeys, ...propsKeys])
123+
if (
124+
(vnode as any).flushId ||
125+
oldComponent.fn !== newComponent.fn ||
126+
oldPropsKeys.length !== propsKeys.length
127+
) {
128+
oldComponent.tree.track(oldComponent.onFlush)
129+
const newNode = (newComponent.fn as any)(props, overmind)
130+
oldComponent.tree.stopTracking()
131+
copyToComponent(newNode, vnode, Object.assign(oldComponent, newComponent))
133132

134-
for (let key of keys) {
135-
if (oldProps[key] !== props[key]) {
136-
copyVNode((newComponent.fn as any)(props, overmind), vnode)
137-
return
138-
}
139-
}
133+
return
134+
}
140135

141-
copyVNode(oldVnode, vnode)
142-
} catch (error) {
143-
console.log(error.message)
136+
const keys = new Set<string>([...oldPropsKeys, ...propsKeys])
137+
138+
for (let key of keys) {
139+
if (oldProps[key] !== props[key]) {
140+
oldComponent.tree.track(oldComponent.onFlush)
141+
const newNode = (newComponent.fn as any)(props, overmind)
142+
oldComponent.tree.stopTracking()
143+
copyToComponent(newNode, vnode, Object.assign(oldComponent, newComponent))
144+
145+
return
146+
}
144147
}
148+
149+
copyToComponent(oldVnode, vnode, Object.assign(oldComponent, newComponent))
145150
}
146151

147-
function postpatch(prevNode, nextNode) {
152+
function postpatch(_, nextNode) {
148153
try {
149-
nextNode.data.component = prevNode.data.component
154+
const component = nextNode.data.component
155+
if (nextNode.flushId) {
156+
getApp().eventHub.emitAsync(EventType.COMPONENT_UPDATE, {
157+
componentId: component.componentId,
158+
componentInstanceId: component.componentInstanceId,
159+
name: component.name,
160+
flushId: component.flushId,
161+
paths: Array.from(component.tree.pathDependencies) as any,
162+
})
163+
}
150164
} catch (error) {
151165
console.log(error.message)
152166
}
153167
}
154168

155169
function destroy(node) {
156170
try {
157-
getApp().disposeTree(node.data.component.tree)
171+
getApp().proxyStateTree.disposeTree(node.data.component.tree)
158172
} catch (error) {
159173
console.log(error.message)
160174
}
@@ -174,13 +188,10 @@ export const component = (component, props, children) => {
174188
component: {
175189
fn: component,
176190
name,
177-
state: undefined,
178-
changeState: null,
179191
props: {
180192
...props,
181193
children: children.length ? children : null,
182194
},
183-
tree: getApp().proxyStateTree.getTrackStateTree(),
184195
},
185196
})
186197
}
Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import h from 'snabbdom/h'
22

33
import { component } from './component'
4-
import { SELF, getName, normalizeAttrs } from './utils'
4+
import { flatten, normalizeAttrs } from './utils'
55

66
export const createElement = (tag, attrs, ...children) => {
77
if (typeof tag === 'function') {
88
return component(tag, attrs, children)
99
}
1010

11-
return h(
12-
tag,
13-
normalizeAttrs(tag, attrs),
14-
children.reduce((aggr, item) => aggr.concat(item), [])
15-
)
11+
return h(tag, normalizeAttrs(tag, attrs), flatten(children))
1612
}

packages/node_modules/overmind-components/src/index.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,37 +13,40 @@ export interface IComponent<
1313
overmind: {
1414
state: State
1515
changeState: (newState: State) => void
16-
store: Overmind<Config>['state']
16+
appState: Overmind<Config>['state']
1717
actions: Overmind<Config>['actions']
1818
effects: Overmind<Config>['effects']
19+
reaction: Overmind<Config>['reaction']
1920
}
2021
): any
2122
}
2223

24+
export type Child<P = any> =
25+
| {
26+
[K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K]
27+
? K
28+
: never
29+
}[keyof JSX.IntrinsicElements]
30+
| IComponent<any, any, any>
31+
| null
32+
| string
33+
2334
declare global {
2435
// @ts-ignore
2536
namespace JSX {
2637
interface IntrinsicElements {
2738
self: React.DetailedHTMLProps<
28-
React.HtmlHTMLAttributes<HTMLHtmlElement> & {
29-
state?: any
30-
onMount?: (el: HTMLElement) => void | (() => void)
31-
},
39+
React.HtmlHTMLAttributes<HTMLHtmlElement>,
3240
HTMLHtmlElement
3341
>
3442
}
3543
}
3644
}
3745

3846
declare module 'react' {
39-
interface HTMLAttributes<T>
40-
extends React.AriaAttributes,
41-
React.DOMAttributes<T> {
42-
onInsert?: (el: HTMLElement) => void
43-
onUpdate?: (el: HTMLElement) => void
44-
onDestroy?: (el: HTMLElement) => void
45-
onEnter?: (el: HTMLElement) => void
46-
onLeave?: (el: HTMLElement) => void
47+
interface HTMLAttributes<T> extends React.DOMAttributes<T> {
48+
onMount?: (el: HTMLElement) => void | (() => void)
49+
onEnter?: (el: HTMLElement) => void | (() => void)
4750
class?: {
4851
[key: string]: boolean
4952
}

0 commit comments

Comments
 (0)