From d11774a4957f2f889838a70da3da2f225490e076 Mon Sep 17 00:00:00 2001 From: Guillaume Delahaye Date: Wed, 25 Sep 2019 11:27:39 +0200 Subject: [PATCH] feat(connector): allow override of stateSliceEqualityComparer --- .babelrc | 3 +- package-lock.json | 202 +++++++++++++++++------------- package.json | 3 +- src/components/connector.js | 7 +- test/components/connector.spec.js | 31 ++++- 5 files changed, 155 insertions(+), 91 deletions(-) diff --git a/.babelrc b/.babelrc index d543970..0d8023e 100644 --- a/.babelrc +++ b/.babelrc @@ -10,7 +10,8 @@ "plugins": "transform-runtime", "env": { "test": { - "presets": ["env"] + "presets": ["env"], + "plugins": "transform-object-rest-spread" } } } diff --git a/package-lock.json b/package-lock.json index a3ac78a..cf9cc1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -239,6 +239,50 @@ "rimraf": "^2.5.2" } }, + "@sinonjs/commons": { + "version": "1.6.0", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/@sinonjs/commons/-/commons-1.6.0.tgz", + "integrity": "sha512-w4/WHG7C4WWFyE5geCieFJF6MZkbW4VAriol5KlmQXpAQdxvV0p26sqNZOW6Qyw6Y0l9K4g+cHvvczR2sEEpqg==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/formatio": { + "version": "3.2.1", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/@sinonjs/formatio/-/formatio-3.2.1.tgz", + "integrity": "sha512-tsHvOB24rvyvV2+zKMmPkZ7dXX6LSLKZ7aOtXY6Edklp0uRcgGpOsQTTGTcWViFyx4uhWc6GV8QdnALbIbIdeQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1", + "@sinonjs/samsam": "^3.1.0" + } + }, + "@sinonjs/samsam": { + "version": "3.3.3", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/@sinonjs/samsam/-/samsam-3.3.3.tgz", + "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.3.0", + "array-from": "^2.1.1", + "lodash": "^4.17.15" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + } + } + }, + "@sinonjs/text-encoding": { + "version": "0.7.1", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", + "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", + "dev": true + }, "JSONStream": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.2.tgz", @@ -260,6 +304,7 @@ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -314,6 +359,12 @@ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", "dev": true }, + "array-from": { + "version": "2.1.1", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/array-from/-/array-from-2.1.1.tgz", + "integrity": "sha1-z+nYwmYoudxa7MYqn12PHzUsEZU=", + "dev": true + }, "array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", @@ -585,6 +636,12 @@ "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", "dev": true }, + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", + "dev": true + }, "babel-plugin-syntax-trailing-function-commas": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", @@ -855,6 +912,16 @@ "babel-runtime": "^6.22.0" } }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", + "dev": true, + "requires": { + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" + } + }, "babel-plugin-transform-regenerator": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz", @@ -1577,6 +1644,12 @@ "repeating": "^2.0.0" } }, + "diff": { + "version": "3.5.0", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, "dot-prop": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", @@ -2395,9 +2468,9 @@ "dev": true }, "just-extend": { - "version": "1.1.22", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.22.tgz", - "integrity": "sha1-MzCvdWyralQnAMZLLk5KoGLVL/8=", + "version": "4.0.2", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/just-extend/-/just-extend-4.0.2.tgz", + "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==", "dev": true }, "kind-of": { @@ -2622,11 +2695,18 @@ "integrity": "sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984=", "dev": true }, + "lolex": { + "version": "4.2.0", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/lolex/-/lolex-4.2.0.tgz", + "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", + "dev": true + }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true + "dev": true, + "optional": true }, "loose-envify": { "version": "1.3.1", @@ -2830,39 +2910,17 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, - "native-promise-only": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", - "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=", - "dev": true - }, "nise": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.0.1.tgz", - "integrity": "sha1-DakrEKhU6XwPSW9sKEWjASgLPu8=", + "version": "1.5.2", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/nise/-/nise-1.5.2.tgz", + "integrity": "sha512-/6RhOUlicRCbE9s+94qCUsyE+pKlVJ5AhIv+jEE7ESKwnbXqulKZ1FYU+XAtHHWE9TinYvAxDUJAb912PwPoWA==", "dev": true, "requires": { - "formatio": "^1.2.0", - "just-extend": "^1.1.22", - "lolex": "^1.6.0", + "@sinonjs/formatio": "^3.2.1", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "lolex": "^4.1.0", "path-to-regexp": "^1.7.0" - }, - "dependencies": { - "formatio": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", - "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=", - "dev": true, - "requires": { - "samsam": "1.x" - } - }, - "lolex": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz", - "integrity": "sha1-OpoCg0UqR9dDnnJzG54H1zhuSfY=", - "dev": true - } } }, "normalize-path": { @@ -3063,7 +3121,7 @@ }, "path-to-regexp": { "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/path-to-regexp/-/path-to-regexp-1.7.0.tgz", "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", "dev": true, "requires": { @@ -3072,7 +3130,7 @@ "dependencies": { "isarray": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/isarray/-/isarray-0.0.1.tgz", "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true } @@ -3552,12 +3610,6 @@ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "dev": true }, - "samsam": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz", - "integrity": "sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=", - "dev": true - }, "semver": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", @@ -3592,48 +3644,34 @@ "dev": true }, "sinon": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-3.2.1.tgz", - "integrity": "sha512-KY3OLOWpek/I4NGAMHetuutVgS2aRgMR5g5/1LSYvPJ3qo2BopIvk3esFztPxF40RWf/NNNJzdFPriSkXUVK3A==", - "dev": true, - "requires": { - "diff": "^3.1.0", - "formatio": "1.2.0", - "lolex": "^2.1.2", - "native-promise-only": "^0.8.1", - "nise": "^1.0.1", - "path-to-regexp": "^1.7.0", - "samsam": "^1.1.3", - "text-encoding": "0.6.4", - "type-detect": "^4.0.0" + "version": "7.5.0", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/sinon/-/sinon-7.5.0.tgz", + "integrity": "sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.4.0", + "@sinonjs/formatio": "^3.2.1", + "@sinonjs/samsam": "^3.3.3", + "diff": "^3.5.0", + "lolex": "^4.2.0", + "nise": "^1.5.2", + "supports-color": "^5.5.0" }, "dependencies": { - "diff": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.0.tgz", - "integrity": "sha512-w0XZubFWn0Adlsapj9EAWX0FqWdO4tz8kc3RiYdWLh4k/V8PTb6i0SMgXt0vRM3zyKnT8tKO7mUlieRQHIjMNg==", + "has-flag": { + "version": "3.0.0", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, - "formatio": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", - "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=", + "supports-color": { + "version": "5.5.0", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "samsam": "1.x" + "has-flag": "^3.0.0" } - }, - "lolex": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.1.2.tgz", - "integrity": "sha1-JpS5U8nqTQE+W4v7qJHJkQJbJik=", - "dev": true - }, - "samsam": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.2.1.tgz", - "integrity": "sha1-7dOQk6MYQ3DLhZJDsr3yVefY6mc=", - "dev": true } } }, @@ -3795,12 +3833,6 @@ "integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0=", "dev": true }, - "text-encoding": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", - "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", - "dev": true - }, "text-extensions": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.7.0.tgz", @@ -3854,9 +3886,9 @@ "dev": true }, "type-detect": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz", - "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=", + "version": "4.0.8", + "resolved": "https://afa-nrepo.azure-paas.intraxa/repository/npmjs-group/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, "typedarray": { diff --git a/package.json b/package.json index 6d60223..78ef61a 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@commitlint/cli": "^5.2.8", "@commitlint/config-conventional": "^5.2.3", "babel-core": "^6.25.0", + "babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-env": "^1.6.0", "cross-env": "^5.0.5", @@ -53,7 +54,7 @@ "rollup-plugin-node-resolve": "^3.0.2", "rollup-plugin-replace": "2.0.0", "rollup-plugin-uglify": "^3.0.0", - "sinon": "^3.2.0", + "sinon": "^7.5.0", "standard-version": "^4.2.0" }, "dependencies": { diff --git a/src/components/connector.js b/src/components/connector.js index 8f3fb2f..d7c8fda 100644 --- a/src/components/connector.js +++ b/src/components/connector.js @@ -11,10 +11,10 @@ const defaultMapStateToTarget = () => ({}); const defaultMapDispatchToTarget = dispatch => ({dispatch}); export default function Connector(store) { - return (mapStateToTarget, mapDispatchToTarget) => { + return (mapStateToTarget, mapDispatchToTarget, stateSliceEqualityComparer = null) => { let finalMapStateToTarget = mapStateToTarget || defaultMapStateToTarget; - + let finalStateSliceEqualityComparer = stateSliceEqualityComparer || shallowEqual; const finalMapDispatchToTarget = isObject(mapDispatchToTarget) && !isFunction(mapDispatchToTarget) ? wrapActionCreators(mapDispatchToTarget) : mapDispatchToTarget || defaultMapDispatchToTarget; @@ -51,7 +51,8 @@ export default function Connector(store) { const unsubscribe = store.subscribe(() => { const nextSlice = getStateSlice(store.getState(), finalMapStateToTarget); - if (!shallowEqual(slice, nextSlice)) { + const shouldUpdateTarget = !finalStateSliceEqualityComparer(slice, nextSlice); + if (shouldUpdateTarget) { updateTarget(target, nextSlice, boundActionCreators, slice); slice = nextSlice; } diff --git a/test/components/connector.spec.js b/test/components/connector.spec.js index e2da408..79d219e 100644 --- a/test/components/connector.spec.js +++ b/test/components/connector.spec.js @@ -1,5 +1,5 @@ import expect from 'expect'; -let sinon = require('sinon'); +import sinon from 'sinon'; import { createStore } from 'redux'; import Connector from '../../src/components/connector'; import isFunction from 'lodash/isFunction'; @@ -74,6 +74,35 @@ describe('Connector', () => { }); + it('should not update targetObj when customStateEqualityComparer return true', () => { + const stateEqualityComparer = sinon.fake.returns(true); + connect(state => state, null, stateEqualityComparer.fake)(targetObj); + store.dispatch({ type: 'ACTION', payload: 5}); + + expect(stateEqualityComparer.calledOnceWith({ + ...defaultState + }, { + ...defaultState, + baz: 5 + })); + expect(targetObj.baz).toBe(5); + }); + + it('should update the target (Object) when customStateEqualityComparer return false', () => { + const stateEqualityComparer = sinon.fake.returns(false); + connect(state => state, null, stateEqualityComparer.fake)(targetObj); + + store.dispatch({ type: 'ACTION', payload: 5}); + + expect(stateEqualityComparer.calledOnceWith({ + ...defaultState, + }, { + ...defaultState, + baz: 5 + })); + expect(targetObj.baz).toBe(5); + }); + it('should update the target (Object) if a function is returned instead of an object', () => { connect(state => state => state)(targetObj); store.dispatch({ type: 'ACTION', payload: 5 });