Skip to content

Commit f05ecf2

Browse files
committed
Adding withHandlers
1 parent 78dee32 commit f05ecf2

File tree

5 files changed

+117
-8
lines changed

5 files changed

+117
-8
lines changed

demo/demos/WithHandlersDemo.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import * as React from 'react';
2+
import { withState } from '../../src/lib/withState';
3+
import { ChainableComponent } from '../../src/ChainableComponent';
4+
import Step from '../Step';
5+
import { DoBuilder } from '../../src';
6+
import { withHandler } from '../../src/lib/withHandlers';
7+
8+
export const WithHandlersDemo =
9+
DoBuilder
10+
.bind('b', withState(0))
11+
.bind('a', withState(0))
12+
.bindL('handler', ({a, b}) => {
13+
console.log('handler', a.value, b.value)
14+
return withHandler(() => alert(`A's count is : ${a.value}`), [b.value])
15+
})
16+
.done()
17+
.render(({a, b, handler}) => (
18+
<div>
19+
<div>a: {a.value} <button onClick={() => a.update(a.value + 1)}>+</button></div>
20+
<div>b: {b.value} <button onClick={() => b.update(b.value + 1)}>+</button></div>
21+
<button onClick={handler}>alert</button>
22+
</div>
23+
))
24+
25+
export default () => (
26+
<Step title="WithHandlersDemo Demo">
27+
<pre className='code-sample'>
28+
{``}
29+
</pre>
30+
{WithHandlersDemo}
31+
</Step>
32+
);

src/Dependencies.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
2+
type Dependency<A> = {
3+
_type: '@@ChainableComponentsDependency',
4+
value: A,
5+
equals: (l: A, r: A) => boolean
6+
}
7+
8+
function isDep<T>(a: any): a is Dependency<T> {
9+
return a.type === '@@ChainableComponentsDependency'
10+
}
11+
12+
type Dependencies = (any | Dependency<any>)[]
13+
14+
export function equals(l: Dependencies, r: Dependencies): boolean {
15+
return l.every((dep, i) => {
16+
const rightDep = r[i]
17+
if(isDep(dep)) {
18+
if(isDep(rightDep)) {
19+
return dep.equals(dep.value, rightDep.value)
20+
} else {
21+
return false;
22+
}
23+
} else {
24+
return dep === rightDep;
25+
}
26+
})
27+
}
28+
29+
export function itemEquals(l: any, r: any): boolean {
30+
if(l && isDep(l) ) {
31+
}
32+
return l.equals(l.value, r.value)
33+
}
34+
35+
export function depEquals<A>(l: Dependency<A>, r: Dependency<A>): boolean {
36+
return l.equals(l.value, r.value)
37+
}

src/lib/test.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<State>
2+
{state => (
3+
<WithHandler handler={() => foo(state)} deps={[state]}>
4+
{handler => (
5+
<div onClick={handler}></div>
6+
)}
7+
</WithHandler>
8+
)}
9+
</State>

src/lib/withHandlers.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { ChainableComponent, fromRenderProp, RenderPropsProps } from '../ChainableComponent';
2+
import { withLifecycle } from './withLifecycle';
3+
import { equals } from '../Dependencies';
4+
5+
export function withHandler<F extends Function>(cb: F, dependencies: any[]): ChainableComponent<F> {
6+
return withLifecycle<[F, any[]]>({
7+
init: () => ([cb, dependencies]),
8+
componentWillUpdate: ([f,deps]) => {
9+
if(equals(deps, dependencies)) {
10+
console.log('still equal, ', deps, dependencies)
11+
return [f, deps];
12+
} else {
13+
console.log('not equal! ', deps, dependencies)
14+
return [cb, dependencies];
15+
}
16+
}
17+
}).map(a => a[0])
18+
}

src/lib/withLifecycle.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ import {
1010
* Configuration for the withLifecycle chainable component
1111
*/
1212
export type WithLifecycleOptions<C> = {
13-
componentDidMount: () => C;
14-
componentWillUnmount?: (c: C) => void;
13+
init: () => C;
14+
componentDidMount?: () => C;
15+
componentWillUnmount?: (c: C) => C;
16+
componentWillUpdate?: (c: C) => C;
1517
shouldComponentUpdate?: (c: C) => boolean;
1618
};
1719

18-
type WithLifecycleProps<C> = RenderPropsProps<WithLifecycleOptions<C>, {}>;
20+
type WithLifecycleProps<C> = RenderPropsProps<WithLifecycleOptions<C>, C>;
1921

2022
/**
2123
* A Render Prop component that mimics the react Component API
@@ -24,25 +26,36 @@ export class WithLifecycle<C> extends React.Component<
2426
WithLifecycleProps<C>,
2527
{}
2628
> {
29+
value: C
2730
constructor(props: WithLifecycleProps<C>) {
2831
super(props);
32+
this.value = this.props.init();
2933
}
3034

3135
componentDidMount() {
32-
this.context = this.props.componentDidMount();
36+
if(this.props.componentDidMount){
37+
this.value = this.props.componentDidMount();
38+
}
3339
}
3440
componentWillUnmount() {
3541
this.props.componentWillUnmount &&
36-
this.props.componentWillUnmount(this.context);
42+
this.props.componentWillUnmount(this.value);
3743
}
3844
shouldComponentUpdate() {
3945
return this.props.shouldComponentUpdate
40-
? this.props.shouldComponentUpdate(this.context)
46+
? this.props.shouldComponentUpdate(this.value)
4147
: true;
4248
}
4349

50+
UNSAFE_componentWillUpdate(nextProps: WithLifecycleProps<C>) {
51+
if(nextProps.componentWillUpdate){
52+
this.value = nextProps.componentWillUpdate(this.value);
53+
}
54+
}
55+
4456
render() {
45-
return this.props.children({});
57+
console.log('Rendering?', this.value)
58+
return this.props.children(this.value);
4659
}
4760
}
4861

@@ -52,6 +65,6 @@ export class WithLifecycle<C> extends React.Component<
5265
*/
5366
export function withLifecycle<C>(
5467
options: WithLifecycleOptions<C>
55-
): ChainableComponent<{}> {
68+
): ChainableComponent<C> {
5669
return fromRenderProp<WithLifecycleProps<C>>(WithLifecycle, options);
5770
}

0 commit comments

Comments
 (0)