Skip to content

Commit 5d8fc93

Browse files
Merge pull request cerebral#179 from cerebral/reactImprovements
React improvements
2 parents f5a5f1d + 601d48f commit 5d8fc93

File tree

4 files changed

+287
-99
lines changed

4 files changed

+287
-99
lines changed

packages/node_modules/overmind-react/src/index.test.tsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,55 @@ describe('React', () => {
134134
expect(ConnectedFoo.name).toBe('ConnectFooComponent')
135135
expect(ConnectedBar.name).toBe('ConnectBarComponent')
136136
})
137+
138+
test('should allow using shouldComponentUpdate', () => {
139+
let renderCount = 0
140+
141+
const doThis: Action = ({ state }) => {
142+
state.foo = 'bar2'
143+
}
144+
const config = {
145+
state: {
146+
foo: 'bar',
147+
},
148+
actions: {
149+
doThis,
150+
},
151+
}
152+
153+
type IConfig = {
154+
state: {
155+
foo: typeof config.state.foo
156+
}
157+
actions: {
158+
doThis: typeof doThis
159+
}
160+
}
161+
162+
const app = new Overmind(config)
163+
164+
type Action<Input = void> = TAction<IConfig, Input>
165+
166+
const connect = createConnect(app)
167+
168+
class FooComponent extends React.Component<TConnect<typeof config>> {
169+
shouldComponentUpdate(nextProps) {
170+
return this.props.overmind !== nextProps.overmind
171+
}
172+
render() {
173+
renderCount++
174+
return <h1>{this.props.overmind.state.foo}</h1>
175+
}
176+
}
177+
178+
const ConnectedFoo = connect(FooComponent)
179+
180+
const tree = renderer.create(<ConnectedFoo />).toJSON()
181+
182+
expect(renderCount).toBe(1)
183+
184+
app.actions.doThis()
185+
186+
expect(renderCount).toBe(2)
187+
})
137188
})

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

Lines changed: 176 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import {
33
// @ts-ignore
44
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
55
ClassicComponentClass,
6+
Component,
67
ComponentClass,
78
createElement,
8-
PureComponent,
99
StatelessComponent,
1010
// @ts-ignore
1111
useEffect,
@@ -15,6 +15,8 @@ import {
1515
} from 'react'
1616
import { IMutation } from 'proxy-state-tree'
1717

18+
const IS_PRODUCTION = process.env.NODE_ENV === 'production'
19+
1820
export type IReactComponent<P = any> =
1921
| StatelessComponent<P>
2022
| ComponentClass<P>
@@ -66,48 +68,62 @@ export const createHook = <A extends Overmind<Configuration>>(
6668
const [tree, updateComponent] = useState<any>(() =>
6769
(overmind as any).proxyStateTree.getTrackStateTree()
6870
)
69-
const [debugging] = useState<any>(() => ({
70-
isFirstRender: true,
71-
currentFlushId: 0,
72-
componentInstanceId: currentComponentInstanceId++,
73-
}))
74-
75-
tree.track((mutations, paths, flushId) => {
76-
debugging.currentFlushId = flushId
77-
updateComponent((state) => state)
78-
})
79-
80-
useLayoutEffect(() => {
81-
if (debugging.isFirstRender) {
82-
overmind.eventHub.emitAsync(EventType.COMPONENT_ADD, {
83-
componentId: component.__componentId,
84-
componentInstanceId: debugging.componentInstanceId,
85-
name,
86-
paths: Array.from(tree.pathDependencies) as any,
87-
})
88-
debugging.isFirstRender = false
89-
} else {
90-
overmind.eventHub.emitAsync(EventType.COMPONENT_UPDATE, {
91-
componentId: component.__componentId,
92-
componentInstanceId: debugging.componentInstanceId,
93-
name,
94-
flushId: debugging.currentFlushId,
95-
paths: Array.from(tree.pathDependencies as Set<string>),
96-
})
97-
}
98-
})
99-
100-
useEffect(
101-
() => () => {
102-
;(overmind as any).proxyStateTree.disposeTree(tree)
103-
overmind.eventHub.emitAsync(EventType.COMPONENT_REMOVE, {
104-
componentId: component.__componentId,
105-
componentInstanceId: debugging.componentInstanceId,
106-
name,
107-
})
108-
},
109-
[]
110-
)
71+
72+
if (IS_PRODUCTION) {
73+
tree.track(() => {
74+
updateComponent((state) => state)
75+
})
76+
77+
useEffect(
78+
() => () => {
79+
;(overmind as any).proxyStateTree.disposeTree(tree)
80+
},
81+
[]
82+
)
83+
} else {
84+
const [debugging] = useState<any>(() => ({
85+
isFirstRender: true,
86+
currentFlushId: 0,
87+
componentInstanceId: currentComponentInstanceId++,
88+
}))
89+
90+
tree.track((mutations, paths, flushId) => {
91+
debugging.currentFlushId = flushId
92+
updateComponent((state) => state)
93+
})
94+
95+
useLayoutEffect(() => {
96+
if (debugging.isFirstRender) {
97+
overmind.eventHub.emitAsync(EventType.COMPONENT_ADD, {
98+
componentId: component.__componentId,
99+
componentInstanceId: debugging.componentInstanceId,
100+
name,
101+
paths: Array.from(tree.pathDependencies) as any,
102+
})
103+
debugging.isFirstRender = false
104+
} else {
105+
overmind.eventHub.emitAsync(EventType.COMPONENT_UPDATE, {
106+
componentId: component.__componentId,
107+
componentInstanceId: debugging.componentInstanceId,
108+
name,
109+
flushId: debugging.currentFlushId,
110+
paths: Array.from(tree.pathDependencies as Set<string>),
111+
})
112+
}
113+
})
114+
115+
useEffect(
116+
() => () => {
117+
;(overmind as any).proxyStateTree.disposeTree(tree)
118+
overmind.eventHub.emitAsync(EventType.COMPONENT_REMOVE, {
119+
componentId: component.__componentId,
120+
componentInstanceId: debugging.componentInstanceId,
121+
name,
122+
})
123+
},
124+
[]
125+
)
126+
}
111127

112128
return {
113129
state: tree.state,
@@ -144,72 +160,137 @@ export const createConnect = <A extends Overmind<Configuration>>(
144160
}
145161
}
146162

147-
class HOC extends PureComponent {
148-
tree = (overmind as any).proxyStateTree.getTrackStateTree()
149-
componentInstanceId = componentInstanceId++
150-
currentFlushId = 0
151-
componentDidMount() {
152-
overmind.eventHub.emitAsync(EventType.COMPONENT_ADD, {
153-
componentId: populatedComponent.__componentId,
154-
componentInstanceId: this.componentInstanceId,
155-
name,
156-
paths: Array.from(this.tree.pathDependencies) as any,
157-
})
158-
}
159-
componentDidUpdate() {
160-
overmind.eventHub.emitAsync(EventType.COMPONENT_UPDATE, {
161-
componentId: populatedComponent.__componentId,
162-
componentInstanceId: this.componentInstanceId,
163-
name,
164-
flushId: this.currentFlushId,
165-
paths: Array.from(this.tree.pathDependencies as Set<string>),
166-
})
167-
}
168-
componentWillUnmount() {
169-
;(overmind as any).proxyStateTree.disposeTree(this.tree)
170-
overmind.eventHub.emitAsync(EventType.COMPONENT_REMOVE, {
171-
componentId: populatedComponent.__componentId,
172-
componentInstanceId: this.componentInstanceId,
173-
name,
174-
})
175-
}
176-
onUpdate = (mutatons, paths, flushId) => {
177-
this.currentFlushId = flushId
178-
this.forceUpdate()
179-
}
180-
render() {
181-
if (isClassComponent) {
163+
if (IS_PRODUCTION) {
164+
class HOC extends Component {
165+
tree = (overmind as any).proxyStateTree.getTrackStateTree()
166+
state: {
167+
overmind: any
168+
}
169+
constructor(props) {
170+
super(props)
171+
this.state = {
172+
overmind: {
173+
state: this.tree.state,
174+
actions: overmind.actions,
175+
addMutationListener: overmind.addMutationListener,
176+
onUpdate: this.onUpdate,
177+
tree: this.tree,
178+
},
179+
}
180+
}
181+
componentWillUnmount() {
182+
;(overmind as any).proxyStateTree.disposeTree(this.tree)
183+
}
184+
onUpdate = () => {
185+
this.setState({
186+
overmind: {
187+
state: this.tree.state,
188+
actions: overmind.actions,
189+
addMutationListener: overmind.addMutationListener,
190+
onUpdate: this.onUpdate,
191+
tree: this.tree,
192+
},
193+
})
194+
}
195+
render() {
196+
if (isClassComponent) {
197+
return createElement(component, {
198+
...this.props,
199+
overmind: this.state.overmind,
200+
} as any)
201+
}
202+
203+
this.tree.track(this.onUpdate)
204+
182205
return createElement(component, {
183206
...this.props,
207+
overmind: this.state.overmind,
208+
} as any)
209+
}
210+
}
211+
212+
return HOC as any
213+
} else {
214+
class HOC extends Component {
215+
tree = (overmind as any).proxyStateTree.getTrackStateTree()
216+
componentInstanceId = componentInstanceId++
217+
currentFlushId = 0
218+
state: {
219+
overmind: any
220+
}
221+
constructor(props) {
222+
super(props)
223+
this.state = {
184224
overmind: {
185225
state: this.tree.state,
186226
actions: overmind.actions,
187227
addMutationListener: overmind.addMutationListener,
188228
onUpdate: this.onUpdate,
189229
tree: this.tree,
190230
},
191-
} as any)
231+
}
232+
}
233+
componentDidMount() {
234+
overmind.eventHub.emitAsync(EventType.COMPONENT_ADD, {
235+
componentId: populatedComponent.__componentId,
236+
componentInstanceId: this.componentInstanceId,
237+
name,
238+
paths: Array.from(this.tree.pathDependencies) as any,
239+
})
240+
}
241+
componentDidUpdate() {
242+
overmind.eventHub.emitAsync(EventType.COMPONENT_UPDATE, {
243+
componentId: populatedComponent.__componentId,
244+
componentInstanceId: this.componentInstanceId,
245+
name,
246+
flushId: this.currentFlushId,
247+
paths: Array.from(this.tree.pathDependencies as Set<string>),
248+
})
249+
}
250+
componentWillUnmount() {
251+
;(overmind as any).proxyStateTree.disposeTree(this.tree)
252+
overmind.eventHub.emitAsync(EventType.COMPONENT_REMOVE, {
253+
componentId: populatedComponent.__componentId,
254+
componentInstanceId: this.componentInstanceId,
255+
name,
256+
})
192257
}
258+
onUpdate = (mutatons, paths, flushId) => {
259+
this.currentFlushId = flushId
260+
this.setState({
261+
overmind: {
262+
state: this.tree.state,
263+
actions: overmind.actions,
264+
addMutationListener: overmind.addMutationListener,
265+
onUpdate: this.onUpdate,
266+
tree: this.tree,
267+
},
268+
})
269+
}
270+
render() {
271+
if (isClassComponent) {
272+
return createElement(component, {
273+
...this.props,
274+
overmind: this.state.overmind,
275+
} as any)
276+
}
193277

194-
this.tree.track(this.onUpdate)
278+
this.tree.track(this.onUpdate)
195279

196-
return createElement(component, {
197-
...this.props,
198-
overmind: {
199-
state: this.tree.state,
200-
actions: overmind.actions,
201-
addMutationListener: overmind.addMutationListener,
202-
},
203-
} as any)
280+
return createElement(component, {
281+
...this.props,
282+
overmind: this.state.overmind,
283+
} as any)
284+
}
204285
}
205-
}
206286

207-
Object.defineProperties(HOC, {
208-
name: {
209-
value: 'Connect' + (component.displayName || component.name || ''),
210-
},
211-
})
287+
Object.defineProperties(HOC, {
288+
name: {
289+
value: 'Connect' + (component.displayName || component.name || ''),
290+
},
291+
})
212292

213-
return HOC as any
293+
return HOC as any
294+
}
214295
}
215296
}

0 commit comments

Comments
 (0)