From c54d8b7bd9d699a056fb608281912a2a455902bd Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Fri, 16 Feb 2018 01:31:24 +0000 Subject: [PATCH] feat: implement shouldUpdate() and pure() --- docs/en/Inversion.md | 3 ++- package.json | 3 ++- src/ShouldUpdate/__story__/Example1.tsx | 2 +- src/ShouldUpdate/__story__/Example2.tsx | 19 +++++++++++++++++++ src/ShouldUpdate/__story__/Example3.tsx | 19 +++++++++++++++++++ src/ShouldUpdate/__story__/Example4.tsx | 23 +++++++++++++++++++++++ src/ShouldUpdate/__story__/story.tsx | 15 ++++++++++++++- src/ShouldUpdate/index.ts | 4 ++++ src/pure.ts | 4 ++++ src/util/shallowEqual.ts | 21 +++++++++++++++++++++ 10 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 src/ShouldUpdate/__story__/Example2.tsx create mode 100644 src/ShouldUpdate/__story__/Example3.tsx create mode 100644 src/ShouldUpdate/__story__/Example4.tsx create mode 100644 src/pure.ts create mode 100644 src/util/shallowEqual.ts diff --git a/docs/en/Inversion.md b/docs/en/Inversion.md index 8370bbdf..a232fd8e 100644 --- a/docs/en/Inversion.md +++ b/docs/en/Inversion.md @@ -15,9 +15,10 @@ This effectively allows you to have in JSX tree without having to write stateful React components. - - [`invert()`](./invert.md) and [``](./invert.md#inverted) — inverts DOM element `ref`. - [``](./State.md) — inverts `.state` and `.setState()` method. - [``](./Toggle.md), [``](./Flipflop.md), [``](./Value.md), [``](./Counter.md), [``](./List.md), and [``](./Map.md) + - [``](./ShouldUpdate.md) — inverts `.shouldComponentUpdate()` life-cycle method. + - [`invert()`](./invert.md) and [``](./invert.md#inverted) — inverts DOM element `ref` reference. ## Example diff --git a/package.json b/package.json index 46ea0634..7903e985 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,8 @@ "freestyler-context": "^1.3.3", "freestyler-renderer": "^1.3.2", "screenfull": "^3.3.2", - "fast-extend": "0.0.2" + "fast-extend": "1.0.2", + "fast-shallow-equal": "0.1.1" }, "peerDependencies": { "react": "*", diff --git a/src/ShouldUpdate/__story__/Example1.tsx b/src/ShouldUpdate/__story__/Example1.tsx index fe4d64c3..00ed8b17 100644 --- a/src/ShouldUpdate/__story__/Example1.tsx +++ b/src/ShouldUpdate/__story__/Example1.tsx @@ -15,4 +15,4 @@ export class Example1 extends Component { ); } -} \ No newline at end of file +} diff --git a/src/ShouldUpdate/__story__/Example2.tsx b/src/ShouldUpdate/__story__/Example2.tsx new file mode 100644 index 00000000..a9e3848b --- /dev/null +++ b/src/ShouldUpdate/__story__/Example2.tsx @@ -0,0 +1,19 @@ +import {Component, createElement as h} from 'react'; +import {shouldUpdate} from '..'; + +const Print = ({cnt}) => Click me ({cnt}); +const LazyPrint = shouldUpdate((props) => props.cnt > 3)(Print); + +export class Example2 extends Component { + state = { + cnt: 0 + }; + + render () { + return ( +
this.setState({cnt: this.state.cnt + 1})}> + +
+ ); + } +} diff --git a/src/ShouldUpdate/__story__/Example3.tsx b/src/ShouldUpdate/__story__/Example3.tsx new file mode 100644 index 00000000..75ea9fef --- /dev/null +++ b/src/ShouldUpdate/__story__/Example3.tsx @@ -0,0 +1,19 @@ +import {Component, createElement as h} from 'react'; +import {shouldUpdate} from '..'; + +const Print = ({cnt}) => Click me ({cnt}); +const LazyPrint = shouldUpdate((props) => !(props.cnt % 3))(Print); + +export class Example3 extends Component { + state = { + cnt: 0 + }; + + render () { + return ( +
this.setState({cnt: this.state.cnt + 1})}> + +
+ ); + } +} diff --git a/src/ShouldUpdate/__story__/Example4.tsx b/src/ShouldUpdate/__story__/Example4.tsx new file mode 100644 index 00000000..de03c5f8 --- /dev/null +++ b/src/ShouldUpdate/__story__/Example4.tsx @@ -0,0 +1,23 @@ +import {Component, createElement as h} from 'react'; +import {pure} from '../../pure'; + +const Print = ({cnt}) => { + console.log('RENDERING'); + + return Click me ({cnt}); +}; +const LazyPrint = pure(Print); + +export class Example4 extends Component { + state = { + cnt: 0 + }; + + render () { + return ( +
this.setState({cnt: this.state.cnt + 1})}> + +
+ ); + } +} diff --git a/src/ShouldUpdate/__story__/story.tsx b/src/ShouldUpdate/__story__/story.tsx index 9c6cf055..0ef8f813 100644 --- a/src/ShouldUpdate/__story__/story.tsx +++ b/src/ShouldUpdate/__story__/story.tsx @@ -2,9 +2,12 @@ import {Component, createElement as h} from 'react'; import {storiesOf} from '@storybook/react'; import {action} from '@storybook/addon-actions'; import {linkTo} from '@storybook/addon-links'; -import {ShouldUpdate} from '..'; +import {ShouldUpdate, shouldUpdate} from '..'; import ShowDocs from '../../../.storybook/ShowDocs' import {Example1} from './Example1'; +import {Example2} from './Example2'; +import {Example3} from './Example3'; +import {Example4} from './Example4'; storiesOf('Inversion/ShouldUpdate', module) // .add('Documentation', () => h(ShowDocs, {md: require('../../../docs/en/ShouldUpdate.md')})) @@ -14,3 +17,13 @@ storiesOf('Inversion/ShouldUpdate', module) ) ) .add('When greater than 3', () => ) + .add('Passes props', () => ( + true} props={{foo: 'bar'}}>{({foo}) => +
+ {foo} +
+ }
+ )) + .add('HOC - greater than 3', () => ) + .add('HOC - increments of 3', () => ) + .add('pure()', () => ) diff --git a/src/ShouldUpdate/index.ts b/src/ShouldUpdate/index.ts index d52d148a..a010c9b9 100644 --- a/src/ShouldUpdate/index.ts +++ b/src/ShouldUpdate/index.ts @@ -1,4 +1,5 @@ import {Component} from 'react'; +import {h} from '../util'; import renderProp from '../util/renderProp'; export interface IShouldUpdateProps { @@ -18,3 +19,6 @@ export class ShouldUpdate extends Component (Comp) => (props) => + h(ShouldUpdate, {when, props}, h(Comp, props)); diff --git a/src/pure.ts b/src/pure.ts new file mode 100644 index 00000000..894c5a2f --- /dev/null +++ b/src/pure.ts @@ -0,0 +1,4 @@ +import {equal} from 'fast-shallow-equal'; +import {shouldUpdate} from './ShouldUpdate'; + +export const pure = shouldUpdate((a, b) => !equal(a, b)); diff --git a/src/util/shallowEqual.ts b/src/util/shallowEqual.ts new file mode 100644 index 00000000..fb8b68ae --- /dev/null +++ b/src/util/shallowEqual.ts @@ -0,0 +1,21 @@ +const keyList = Object.keys; +const hasProp = Object.prototype.hasOwnProperty; + +const shallowEqual: (a, b) => boolean = (a, b) => { + if (a === b) return true; + if (!a || !b) return false; + if (!(a instanceof Object) || !(b instanceof Object)) return false; + + const keys = keyList(a); + const length = keys.length; + + for (let i = 0; i < length; i++) + if (!(keys[i] in b)) return false; + + for (let i = 0; i < length; i++) + if (a[keys[i]] !== b[keys[i]]) return false; + + return length === keyList(b).length; +} + +export default \ No newline at end of file