Skip to content

Commit

Permalink
Adding withHandlers
Browse files Browse the repository at this point in the history
  • Loading branch information
pfgray committed Apr 25, 2019
1 parent 78dee32 commit f05ecf2
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 8 deletions.
32 changes: 32 additions & 0 deletions demo/demos/WithHandlersDemo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as React from 'react';
import { withState } from '../../src/lib/withState';
import { ChainableComponent } from '../../src/ChainableComponent';
import Step from '../Step';
import { DoBuilder } from '../../src';
import { withHandler } from '../../src/lib/withHandlers';

export const WithHandlersDemo =
DoBuilder
.bind('b', withState(0))
.bind('a', withState(0))
.bindL('handler', ({a, b}) => {
console.log('handler', a.value, b.value)
return withHandler(() => alert(`A's count is : ${a.value}`), [b.value])
})
.done()
.render(({a, b, handler}) => (
<div>
<div>a: {a.value} <button onClick={() => a.update(a.value + 1)}>+</button></div>
<div>b: {b.value} <button onClick={() => b.update(b.value + 1)}>+</button></div>
<button onClick={handler}>alert</button>
</div>
))

export default () => (
<Step title="WithHandlersDemo Demo">
<pre className='code-sample'>
{``}
</pre>
{WithHandlersDemo}
</Step>
);
37 changes: 37 additions & 0 deletions src/Dependencies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

type Dependency<A> = {
_type: '@@ChainableComponentsDependency',
value: A,
equals: (l: A, r: A) => boolean
}

function isDep<T>(a: any): a is Dependency<T> {
return a.type === '@@ChainableComponentsDependency'
}

type Dependencies = (any | Dependency<any>)[]

export function equals(l: Dependencies, r: Dependencies): boolean {
return l.every((dep, i) => {
const rightDep = r[i]
if(isDep(dep)) {
if(isDep(rightDep)) {
return dep.equals(dep.value, rightDep.value)
} else {
return false;
}
} else {
return dep === rightDep;
}
})
}

export function itemEquals(l: any, r: any): boolean {
if(l && isDep(l) ) {
}
return l.equals(l.value, r.value)
}

export function depEquals<A>(l: Dependency<A>, r: Dependency<A>): boolean {
return l.equals(l.value, r.value)
}
9 changes: 9 additions & 0 deletions src/lib/test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<State>
{state => (
<WithHandler handler={() => foo(state)} deps={[state]}>
{handler => (
<div onClick={handler}></div>
)}
</WithHandler>
)}
</State>
18 changes: 18 additions & 0 deletions src/lib/withHandlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ChainableComponent, fromRenderProp, RenderPropsProps } from '../ChainableComponent';
import { withLifecycle } from './withLifecycle';
import { equals } from '../Dependencies';

export function withHandler<F extends Function>(cb: F, dependencies: any[]): ChainableComponent<F> {
return withLifecycle<[F, any[]]>({
init: () => ([cb, dependencies]),
componentWillUpdate: ([f,deps]) => {
if(equals(deps, dependencies)) {
console.log('still equal, ', deps, dependencies)
return [f, deps];
} else {
console.log('not equal! ', deps, dependencies)
return [cb, dependencies];
}
}
}).map(a => a[0])
}
29 changes: 21 additions & 8 deletions src/lib/withLifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import {
* Configuration for the withLifecycle chainable component
*/
export type WithLifecycleOptions<C> = {
componentDidMount: () => C;
componentWillUnmount?: (c: C) => void;
init: () => C;
componentDidMount?: () => C;
componentWillUnmount?: (c: C) => C;
componentWillUpdate?: (c: C) => C;
shouldComponentUpdate?: (c: C) => boolean;
};

type WithLifecycleProps<C> = RenderPropsProps<WithLifecycleOptions<C>, {}>;
type WithLifecycleProps<C> = RenderPropsProps<WithLifecycleOptions<C>, C>;

/**
* A Render Prop component that mimics the react Component API
Expand All @@ -24,25 +26,36 @@ export class WithLifecycle<C> extends React.Component<
WithLifecycleProps<C>,
{}
> {
value: C
constructor(props: WithLifecycleProps<C>) {
super(props);
this.value = this.props.init();
}

componentDidMount() {
this.context = this.props.componentDidMount();
if(this.props.componentDidMount){
this.value = this.props.componentDidMount();
}
}
componentWillUnmount() {
this.props.componentWillUnmount &&
this.props.componentWillUnmount(this.context);
this.props.componentWillUnmount(this.value);
}
shouldComponentUpdate() {
return this.props.shouldComponentUpdate
? this.props.shouldComponentUpdate(this.context)
? this.props.shouldComponentUpdate(this.value)
: true;
}

UNSAFE_componentWillUpdate(nextProps: WithLifecycleProps<C>) {
if(nextProps.componentWillUpdate){
this.value = nextProps.componentWillUpdate(this.value);
}
}

render() {
return this.props.children({});
console.log('Rendering?', this.value)
return this.props.children(this.value);
}
}

Expand All @@ -52,6 +65,6 @@ export class WithLifecycle<C> extends React.Component<
*/
export function withLifecycle<C>(
options: WithLifecycleOptions<C>
): ChainableComponent<{}> {
): ChainableComponent<C> {
return fromRenderProp<WithLifecycleProps<C>>(WithLifecycle, options);
}

0 comments on commit f05ecf2

Please sign in to comment.