├── .gitignore ├── modules ├── index.js ├── storePlugin.js └── connect.js ├── .npmignore ├── .editorconfig ├── dist ├── commonjs │ ├── storePlugin.js │ ├── index.js │ └── connect.js └── umd │ ├── index.js │ ├── storePlugin.js │ └── connect.js ├── gulpfile.js ├── scripts └── release.js ├── LICENSE ├── package.json ├── README.md ├── .eslintrc └── test └── main.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | jspm_packages/ 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /modules/index.js: -------------------------------------------------------------------------------- 1 | import connect from './connect'; 2 | import storePlugin from './storePlugin'; 3 | 4 | export default { connect, storePlugin }; 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | **/.*, 2 | node_modules, 3 | scripts, 4 | npm-debug.log, 5 | bower.json, 6 | README.md, 7 | CHANGELOG.md, 8 | package.json, 9 | gulpfile.js 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | 9 | charset = utf-8 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /modules/storePlugin.js: -------------------------------------------------------------------------------- 1 | const storePlugin = store => app => { 2 | // TODO: check store is a store indeed 3 | app.set('store', store); 4 | app.set('storeState', store.getState()); 5 | store.subscribe(() => app.set('storeState', store.getState())); 6 | }; 7 | 8 | export default storePlugin; 9 | -------------------------------------------------------------------------------- /dist/commonjs/storePlugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | var storePlugin = function storePlugin(store) { 7 | return function (app) { 8 | // TODO: check store is a store indeed 9 | app.set('store', store); 10 | app.set('storeState', store.getState()); 11 | store.subscribe(function () { 12 | return app.set('storeState', store.getState()); 13 | }); 14 | }; 15 | }; 16 | 17 | exports['default'] = storePlugin; 18 | module.exports = exports['default']; -------------------------------------------------------------------------------- /dist/commonjs/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 8 | 9 | var _connect = require('./connect'); 10 | 11 | var _connect2 = _interopRequireDefault(_connect); 12 | 13 | var _storePlugin = require('./storePlugin'); 14 | 15 | var _storePlugin2 = _interopRequireDefault(_storePlugin); 16 | 17 | exports['default'] = { connect: _connect2['default'], storePlugin: _storePlugin2['default'] }; 18 | module.exports = exports['default']; -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var babel = require('gulp-babel'); 3 | var runSequence = require('run-sequence'); 4 | var rename = require('gulp-rename'); 5 | var del = require('del'); 6 | 7 | var files = [ 8 | 'modules/*.js' 9 | ]; 10 | 11 | function build(modules, dest) { 12 | return function() { 13 | return gulp 14 | .src(files, {base: 'modules'}) 15 | .pipe(babel({modules: modules})) 16 | .pipe(gulp.dest(dest)); 17 | }; 18 | } 19 | gulp.task('clean', function() { 20 | return del(['dist']); 21 | }); 22 | 23 | gulp.task('buildCommonJs', build('common', 'dist/commonjs')); 24 | gulp.task('buildUmd', build('umd', 'dist/umd')); 25 | 26 | gulp.task('build', function() { 27 | return runSequence('clean', ['buildCommonJs', 'buildUmd']); 28 | }); 29 | -------------------------------------------------------------------------------- /scripts/release.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var exec = require('child_process').execSync; 4 | var argv = require('yargs').argv; 5 | var fs = require('fs'); 6 | var path = require('path'); 7 | 8 | var versionType = argv.major ? 'major' : (argv.minor ? 'minor' : 'patch'); 9 | 10 | run('npm install'); 11 | run('npm prune'); 12 | var VERSION = run('npm --no-git-tag-version version ' + versionType, false); 13 | 14 | run('npm run lint'); 15 | run('npm run build'); 16 | run('npm test'); 17 | run('npm run clog'); 18 | 19 | run('git add -A'); 20 | run('git add dist -f'); 21 | run('git commit -m "chore: ' + VERSION + '"'); 22 | run('git tag ' + VERSION); 23 | run('git push origin master'); 24 | run('git push --tags'); 25 | 26 | run('npm publish'); 27 | 28 | function run(cmd, log) { 29 | log = log === undefined ? true : log; 30 | var res = exec(cmd).toString(); 31 | if (log && res) console.log(res); 32 | return res; 33 | } 34 | -------------------------------------------------------------------------------- /dist/umd/index.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['exports', 'module', './connect', './storePlugin'], factory); 4 | } else if (typeof exports !== 'undefined' && typeof module !== 'undefined') { 5 | factory(exports, module, require('./connect'), require('./storePlugin')); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(mod.exports, mod, global.connect, global.storePlugin); 11 | global.index = mod.exports; 12 | } 13 | })(this, function (exports, module, _connect, _storePlugin) { 14 | 'use strict'; 15 | 16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 17 | 18 | var _connect2 = _interopRequireDefault(_connect); 19 | 20 | var _storePlugin2 = _interopRequireDefault(_storePlugin); 21 | 22 | module.exports = { connect: _connect2['default'], storePlugin: _storePlugin2['default'] }; 23 | }); -------------------------------------------------------------------------------- /dist/umd/storePlugin.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['exports', 'module'], factory); 4 | } else if (typeof exports !== 'undefined' && typeof module !== 'undefined') { 5 | factory(exports, module); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(mod.exports, mod); 11 | global.storePlugin = mod.exports; 12 | } 13 | })(this, function (exports, module) { 14 | 'use strict'; 15 | 16 | var storePlugin = function storePlugin(store) { 17 | return function (app) { 18 | // TODO: check store is a store indeed 19 | app.set('store', store); 20 | app.set('storeState', store.getState()); 21 | store.subscribe(function () { 22 | return app.set('storeState', store.getState()); 23 | }); 24 | }; 25 | }; 26 | 27 | module.exports = storePlugin; 28 | }); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Thomas Roch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deku-redux", 3 | "version": "1.0.0", 4 | "description": "Redux bindings for deku", 5 | "main": "dist/commonjs/index.js", 6 | "scripts": { 7 | "test": "_mocha", 8 | "build": "gulp build" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/troch/deku-redux.git" 13 | }, 14 | "keywords": [ 15 | "deku", 16 | "redux" 17 | ], 18 | "author": "Thomas Roch ", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/troch/deku-redux/issues" 22 | }, 23 | "homepage": "https://github.com/troch/deku-redux#readme", 24 | "dependencies": { 25 | "invariant": "^2.1.2", 26 | "is-equal-shallow": "^0.1.3", 27 | "is-plain-object": "^2.0.1" 28 | }, 29 | "devDependencies": { 30 | "component-mock": "0.0.2", 31 | "del": "^2.0.2", 32 | "gulp": "^3.9.0", 33 | "gulp-babel": "^5.3.0", 34 | "gulp-concat": "^2.6.0", 35 | "gulp-rename": "^1.2.2", 36 | "gulp-uglify": "^1.4.2", 37 | "mocha": "^2.3.3", 38 | "run-sequence": "^1.1.4", 39 | "should": "^7.1.1", 40 | "through2": "^2.0.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # deku-redux 2 | 3 | > Bindings for redux in deku. 4 | 5 | ```sh 6 | npm install deku-redux 7 | ``` 8 | 9 | 10 | ### Redux 11 | 12 | Read the docs about Redux: __[Redux docs](http://rackt.org/redux/index.html)__. 13 | If you are familiar with [react-redux](https://github.com/rackt/react-redux), _deku-redux_ is very similar. 14 | 15 | ### storePlugin(store) 16 | 17 | Add your store to your tree with `storePlugin`. It is required for `connect` to be able to get access to your state. 18 | 19 | ```javascript 20 | import { tree, render } from 'deku'; 21 | import element from 'virtual-element'; 22 | import { createStore } from 'redux'; 23 | import { storePlugin } from 'deku-redux'; 24 | import reducers from './reducers'; 25 | import App from './components/App'; 26 | 27 | const store = createStore(reducers); 28 | 29 | const app = tree() 30 | .use(storePlugin(store)) 31 | .mount(element(App)); 32 | 33 | render(app, document.getElementById('app')); 34 | ``` 35 | 36 | ### connect([mapStateToProps], [mapDispatchToProps], [mergeProps]) 37 | 38 | Use `connect` higher-order component for connecting to your state, same as `connect` from [react-redux](https://github.com/rackt/react-redux). 39 | 40 | See __[Redux with React](http://rackt.org/redux/docs/basics/UsageWithReact.html)__ for more information on how to use. 41 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | // Parser 3 | "parser": "babel-eslint", 4 | // ECMA Features 5 | "ecmaFeatures": { 6 | "arrowFunctions": true, 7 | "blockBindings": true, 8 | "classes": true, 9 | "defaultParams": true, 10 | "destructuring": true, 11 | "modules": true, 12 | "objectLiteralComputedProperties": true, 13 | "templateStrings": true 14 | }, 15 | "rules": { 16 | // Possible Errors 17 | "no-dupe-args": 2, 18 | "no-dupe-keys": 2, 19 | "no-empty": 2, 20 | "no-func-assign": 2, 21 | "no-inner-declarations": 2, 22 | "no-unreachable": 2, 23 | "no-unexpected-multiline": 2, 24 | // Best practices 25 | "consistent-return": 0, 26 | "curly": [2, "multi-line"], 27 | "eqeqeq": 2, 28 | "no-else-return": 2, 29 | "no-multi-spaces": 0, 30 | // Strict mode 31 | "strict": 0, 32 | // Variables 33 | "no-shadow": 0, 34 | "no-unused-vars": 0, 35 | "no-use-before-define": 0, 36 | // Style 37 | "brace-style": [2, "1tbs"], 38 | "comma-spacing": [2, {"before": false, "after": true}], 39 | "comma-style": [2, "last"], 40 | "consistent-this": [2, "that"], 41 | "lines-around-comment": [2, {"allowBlockStart": true}], 42 | "key-spacing": 0, 43 | "new-parens": 0, 44 | "quotes": [2, "single", "avoid-escape"], 45 | "no-underscore-dangle": 0, 46 | "no-unneeded-ternary": 2, 47 | "semi": 2, 48 | // ES6 49 | "no-var": 2, 50 | "no-this-before-super": 2, 51 | "object-shorthand": 2, 52 | }, 53 | "env": { 54 | "node": true, 55 | "browser": true 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/main.js: -------------------------------------------------------------------------------- 1 | require('mocha'); 2 | var should = require('should'); 3 | 4 | var Mock = require('component-mock'); 5 | 6 | var redux = require('redux'); 7 | var createStore = redux.createStore; 8 | 9 | var element = require('virtual-element'); 10 | var deku = require('deku'); 11 | var tree = deku.tree; 12 | 13 | var dekuRedux = require('../dist/commonjs'); 14 | var storePlugin = dekuRedux.storePlugin; 15 | var connect = dekuRedux.connect; 16 | 17 | var store = createStore(function(state, action) { 18 | switch (action.type) { 19 | case 'INCREMENT': 20 | return { count: state.count + 1 }; 21 | default: 22 | return state; 23 | } 24 | }, { count: 0 }); 25 | 26 | function increment() { 27 | return { 28 | type: 'INCREMENT' 29 | }; 30 | } 31 | 32 | function render(component) { 33 | return element('div', component.props.count); 34 | } 35 | 36 | describe('deku-redux', function() { 37 | it('should add store and storeState to a deku tree', function() { 38 | var app = tree() 39 | .use(storePlugin(store)) 40 | .mount(element({ render: render})); 41 | 42 | app.sources.store.should.equal(store); 43 | app.sources.storeState.should.eql({ count: 0 }); 44 | 45 | store.dispatch(increment()); 46 | 47 | app.sources.storeState.should.eql({ count: 1 }); 48 | }); 49 | 50 | it('should connect components', function() { 51 | var Component = { render: render }; 52 | function mapStateToProps(state) { 53 | return state; 54 | } 55 | 56 | var app = tree() 57 | .use(storePlugin(store)) 58 | .mount(element(connect(mapStateToProps)(Component))); 59 | 60 | var mock = Mock(app.element.type); 61 | console.log(mock.render()); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /modules/connect.js: -------------------------------------------------------------------------------- 1 | import { bindActionCreators } from 'redux'; 2 | import shallowEquals from 'is-equal-shallow'; 3 | import isPlainObject from 'is-plain-object'; 4 | import invariant from 'invariant'; 5 | 6 | const defaultMapStateToProps = () => ({}); 7 | const defaultMapDispatchToProps = dispatch => ({ dispatch }); 8 | const defaultMergeProps = (stateProps, dispatchProps, parentProps) => ({ 9 | ...parentProps, 10 | ...stateProps, 11 | ...dispatchProps 12 | }); 13 | 14 | const wrapActionCreators = actionCreators => dispatch => bindActionCreators(actionCreators, dispatch); 15 | 16 | 17 | function connect(mapStateToProps = defaultMapStateToProps, mapDispatchToProps = defaultMapDispatchToProps, mergeProps = defaultMergeProps) { 18 | if(isPlainObject(mapDispatchToProps)) { 19 | mapDispatchToProps = wrapActionCreators(mapDispatchToProps); 20 | } 21 | 22 | const mappedStateUseProps = mapStateToProps.length > 1; 23 | const mappedDispatchUseProps = mapDispatchToProps.length > 1; 24 | 25 | const computeStateProps = props => { 26 | const state = props.store.getState(); 27 | const stateProps = mapStateToProps(state, props); 28 | 29 | invariant(isPlainObject(stateProps), '[deku-redux][connect] mapStateToProps must return an object. Instead received %s', stateProps); 30 | 31 | return stateProps; 32 | }; 33 | 34 | const computeDispatchProps = props => { 35 | const dispatch = props.store.dispatch; 36 | const dispatchProps = mapDispatchToProps(dispatch, props); 37 | 38 | invariant(isPlainObject(dispatchProps), '[deku-redux][connect] mapDispatchToProps must return an object. Instead received %s', dispatchProps); 39 | 40 | return dispatchProps; 41 | }; 42 | 43 | return function connectWrapper(Component) { 44 | const connectRegistry = {}; 45 | const getStateProps = id => connectRegistry[id] ? connectRegistry[id].stateProps : null; 46 | const setStateProps = (id, stateProps) => connectRegistry[id].stateProps = stateProps; 47 | const getDispatchProps = id => connectRegistry[id] ? connectRegistry[id].dispatchProps : null; 48 | const setDispatchProps = (id, dispatchProps) => connectRegistry[id].dispatchProps = dispatchProps; 49 | 50 | const ConnectedComponent = { 51 | propTypes: { 52 | store: { source: 'store' }, 53 | storeState: { source: 'storeState' } 54 | }, 55 | 56 | beforeMount({ id, props }, elm, setState) { 57 | invariant(props.store, '[deku-redux][connect] Could not find store. Did you use `storePlugin` on your deku tree?'); 58 | invariant( 59 | props.store.getState && props.store.subscribe && props.store.subscribe, 60 | '[deku-redux][connect] Could not recognise store. Did you use `storePlugin` with a valid redux store?' 61 | ); 62 | 63 | connectRegistry[id] = {}; 64 | setStateProps(id, computeStateProps(props)); 65 | setDispatchProps(id, computeDispatchProps(props)); 66 | }, 67 | 68 | shouldUpdate({ id, props }, nextProps) { 69 | const storeChanged = nextProps.storeState !== props.storeState; 70 | const propsChanged = !shallowEquals(nextProps, props); 71 | 72 | const computeNewStateProps = storeChanged || (propsChanged && mappedStateUseProps); 73 | const computeNewDispatchProps = propsChanged && mappedDispatchUseProps; 74 | 75 | let statePropsChanged = false; 76 | let dispatchPropsChanged = false; 77 | 78 | if (computeNewStateProps) { 79 | const stateProps = computeStateProps(props); 80 | statePropsChanged = !shallowEquals(stateProps, getStateProps(id)); 81 | if (statePropsChanged) { 82 | setStateProps(id, stateProps); 83 | } 84 | } 85 | 86 | if (computeNewDispatchProps) { 87 | const dispatchProps = computeDispatchProps(props); 88 | dispatchPropsChanged = !shallowEquals(dispatchProps, getDispatchProps(id)); 89 | if (dispatchPropsChanged) { 90 | setDispatchProps(id, stateProps); 91 | } 92 | } 93 | 94 | return propsChanged || statePropsChanged || dispatchPropsChanged; 95 | }, 96 | 97 | beforeUnmount({ id }) { 98 | connectRegistry[id] = undefined; 99 | }, 100 | 101 | render({ id, props }) { 102 | // TODO: use _.omit or similar 103 | const parentProps = Object.keys(props) 104 | .filter(prop => prop !== 'store' || prop !== 'storeState') 105 | .reduce((acc, prop) => { 106 | acc[prop] = props[prop]; 107 | return acc; 108 | }, {}); 109 | 110 | const componentProps = mergeProps(parentProps, getStateProps(id), getDispatchProps(id)); 111 | 112 | invariant(isPlainObject(componentProps), '[deku-redux][connect] `mergeProps` function didn\'t return a plain object.'); 113 | 114 | // 115 | return { type: Component, children: props.children, attributes: componentProps }; 116 | } 117 | }; 118 | 119 | return ConnectedComponent; 120 | }; 121 | } 122 | 123 | export default connect; 124 | -------------------------------------------------------------------------------- /dist/commonjs/connect.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 8 | 9 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 10 | 11 | var _redux = require('redux'); 12 | 13 | var _isEqualShallow = require('is-equal-shallow'); 14 | 15 | var _isEqualShallow2 = _interopRequireDefault(_isEqualShallow); 16 | 17 | var _isPlainObject = require('is-plain-object'); 18 | 19 | var _isPlainObject2 = _interopRequireDefault(_isPlainObject); 20 | 21 | var _invariant = require('invariant'); 22 | 23 | var _invariant2 = _interopRequireDefault(_invariant); 24 | 25 | var defaultMapStateToProps = function defaultMapStateToProps() { 26 | return {}; 27 | }; 28 | var defaultMapDispatchToProps = function defaultMapDispatchToProps(dispatch) { 29 | return { dispatch: dispatch }; 30 | }; 31 | var defaultMergeProps = function defaultMergeProps(stateProps, dispatchProps, parentProps) { 32 | return _extends({}, parentProps, stateProps, dispatchProps); 33 | }; 34 | 35 | var wrapActionCreators = function wrapActionCreators(actionCreators) { 36 | return function (dispatch) { 37 | return (0, _redux.bindActionCreators)(actionCreators, dispatch); 38 | }; 39 | }; 40 | 41 | function connect() { 42 | var mapStateToProps = arguments.length <= 0 || arguments[0] === undefined ? defaultMapStateToProps : arguments[0]; 43 | var mapDispatchToProps = arguments.length <= 1 || arguments[1] === undefined ? defaultMapDispatchToProps : arguments[1]; 44 | var mergeProps = arguments.length <= 2 || arguments[2] === undefined ? defaultMergeProps : arguments[2]; 45 | 46 | if ((0, _isPlainObject2['default'])(mapDispatchToProps)) { 47 | mapDispatchToProps = wrapActionCreators(mapDispatchToProps); 48 | } 49 | 50 | var mappedStateUseProps = mapStateToProps.length > 1; 51 | var mappedDispatchUseProps = mapDispatchToProps.length > 1; 52 | 53 | var computeStateProps = function computeStateProps(props) { 54 | var state = props.store.getState(); 55 | var stateProps = mapStateToProps(state, props); 56 | 57 | (0, _invariant2['default'])((0, _isPlainObject2['default'])(stateProps), '[deku-redux][connect] mapStateToProps must return an object. Instead received %s', stateProps); 58 | 59 | return stateProps; 60 | }; 61 | 62 | var computeDispatchProps = function computeDispatchProps(props) { 63 | var dispatch = props.store.dispatch; 64 | var dispatchProps = mapDispatchToProps(dispatch, props); 65 | 66 | (0, _invariant2['default'])((0, _isPlainObject2['default'])(dispatchProps), '[deku-redux][connect] mapDispatchToProps must return an object. Instead received %s', dispatchProps); 67 | 68 | return dispatchProps; 69 | }; 70 | 71 | return function connectWrapper(Component) { 72 | var connectRegistry = {}; 73 | var getStateProps = function getStateProps(id) { 74 | return connectRegistry[id] ? connectRegistry[id].stateProps : null; 75 | }; 76 | var setStateProps = function setStateProps(id, stateProps) { 77 | return connectRegistry[id].stateProps = stateProps; 78 | }; 79 | var getDispatchProps = function getDispatchProps(id) { 80 | return connectRegistry[id] ? connectRegistry[id].dispatchProps : null; 81 | }; 82 | var setDispatchProps = function setDispatchProps(id, dispatchProps) { 83 | return connectRegistry[id].dispatchProps = dispatchProps; 84 | }; 85 | 86 | var ConnectedComponent = { 87 | propTypes: { 88 | store: { source: 'store' }, 89 | storeState: { source: 'storeState' } 90 | }, 91 | 92 | beforeMount: function beforeMount(_ref, elm, setState) { 93 | var id = _ref.id; 94 | var props = _ref.props; 95 | 96 | (0, _invariant2['default'])(props.store, '[deku-redux][connect] Could not find store. Did you use `storePlugin` on your deku tree?'); 97 | (0, _invariant2['default'])(props.store.getState && props.store.subscribe && props.store.subscribe, '[deku-redux][connect] Could not recognise store. Did you use `storePlugin` with a valid redux store?'); 98 | 99 | connectRegistry[id] = {}; 100 | setStateProps(id, computeStateProps(props)); 101 | setDispatchProps(id, computeDispatchProps(props)); 102 | }, 103 | 104 | shouldUpdate: function shouldUpdate(_ref2, nextProps) { 105 | var id = _ref2.id; 106 | var props = _ref2.props; 107 | 108 | var storeChanged = nextProps.storeState !== props.storeState; 109 | var propsChanged = !(0, _isEqualShallow2['default'])(nextProps, props); 110 | 111 | var computeNewStateProps = storeChanged || propsChanged && mappedStateUseProps; 112 | var computeNewDispatchProps = propsChanged && mappedDispatchUseProps; 113 | 114 | var statePropsChanged = false; 115 | var dispatchPropsChanged = false; 116 | 117 | if (computeNewStateProps) { 118 | var _stateProps = computeStateProps(props); 119 | statePropsChanged = !(0, _isEqualShallow2['default'])(_stateProps, getStateProps(id)); 120 | if (statePropsChanged) { 121 | setStateProps(id, _stateProps); 122 | } 123 | } 124 | 125 | if (computeNewDispatchProps) { 126 | var dispatchProps = computeDispatchProps(props); 127 | dispatchPropsChanged = !(0, _isEqualShallow2['default'])(dispatchProps, getDispatchProps(id)); 128 | if (dispatchPropsChanged) { 129 | setDispatchProps(id, stateProps); 130 | } 131 | } 132 | 133 | return propsChanged || statePropsChanged || dispatchPropsChanged; 134 | }, 135 | 136 | beforeUnmount: function beforeUnmount(_ref3) { 137 | var id = _ref3.id; 138 | 139 | connectRegistry[id] = undefined; 140 | }, 141 | 142 | render: function render(_ref4) { 143 | var id = _ref4.id; 144 | var props = _ref4.props; 145 | 146 | // TODO: use _.omit or similar 147 | var parentProps = Object.keys(props).filter(function (prop) { 148 | return prop !== 'store' || prop !== 'storeState'; 149 | }).reduce(function (acc, prop) { 150 | acc[prop] = props[prop]; 151 | return acc; 152 | }, {}); 153 | 154 | var componentProps = mergeProps(parentProps, getStateProps(id), getDispatchProps(id)); 155 | 156 | (0, _invariant2['default'])((0, _isPlainObject2['default'])(componentProps), '[deku-redux][connect] `mergeProps` function didn\'t return a plain object.'); 157 | 158 | // 159 | return { type: Component, children: props.children, attributes: componentProps }; 160 | } 161 | }; 162 | 163 | return ConnectedComponent; 164 | }; 165 | } 166 | 167 | exports['default'] = connect; 168 | module.exports = exports['default']; -------------------------------------------------------------------------------- /dist/umd/connect.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(['exports', 'module', 'redux', 'is-equal-shallow', 'is-plain-object', 'invariant'], factory); 4 | } else if (typeof exports !== 'undefined' && typeof module !== 'undefined') { 5 | factory(exports, module, require('redux'), require('is-equal-shallow'), require('is-plain-object'), require('invariant')); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(mod.exports, mod, global.redux, global.shallowEquals, global.isPlainObject, global.invariant); 11 | global.connect = mod.exports; 12 | } 13 | })(this, function (exports, module, _redux, _isEqualShallow, _isPlainObject, _invariant) { 14 | 'use strict'; 15 | 16 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 17 | 18 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 19 | 20 | var _shallowEquals = _interopRequireDefault(_isEqualShallow); 21 | 22 | var _isPlainObject2 = _interopRequireDefault(_isPlainObject); 23 | 24 | var _invariant2 = _interopRequireDefault(_invariant); 25 | 26 | var defaultMapStateToProps = function defaultMapStateToProps() { 27 | return {}; 28 | }; 29 | var defaultMapDispatchToProps = function defaultMapDispatchToProps(dispatch) { 30 | return { dispatch: dispatch }; 31 | }; 32 | var defaultMergeProps = function defaultMergeProps(stateProps, dispatchProps, parentProps) { 33 | return _extends({}, parentProps, stateProps, dispatchProps); 34 | }; 35 | 36 | var wrapActionCreators = function wrapActionCreators(actionCreators) { 37 | return function (dispatch) { 38 | return (0, _redux.bindActionCreators)(actionCreators, dispatch); 39 | }; 40 | }; 41 | 42 | function connect() { 43 | var mapStateToProps = arguments.length <= 0 || arguments[0] === undefined ? defaultMapStateToProps : arguments[0]; 44 | var mapDispatchToProps = arguments.length <= 1 || arguments[1] === undefined ? defaultMapDispatchToProps : arguments[1]; 45 | var mergeProps = arguments.length <= 2 || arguments[2] === undefined ? defaultMergeProps : arguments[2]; 46 | 47 | if ((0, _isPlainObject2['default'])(mapDispatchToProps)) { 48 | mapDispatchToProps = wrapActionCreators(mapDispatchToProps); 49 | } 50 | 51 | var mappedStateUseProps = mapStateToProps.length > 1; 52 | var mappedDispatchUseProps = mapDispatchToProps.length > 1; 53 | 54 | var computeStateProps = function computeStateProps(props) { 55 | var state = props.store.getState(); 56 | var stateProps = mapStateToProps(state, props); 57 | 58 | (0, _invariant2['default'])((0, _isPlainObject2['default'])(stateProps), '[deku-redux][connect] mapStateToProps must return an object. Instead received %s', stateProps); 59 | 60 | return stateProps; 61 | }; 62 | 63 | var computeDispatchProps = function computeDispatchProps(props) { 64 | var dispatch = props.store.dispatch; 65 | var dispatchProps = mapDispatchToProps(dispatch, props); 66 | 67 | (0, _invariant2['default'])((0, _isPlainObject2['default'])(dispatchProps), '[deku-redux][connect] mapDispatchToProps must return an object. Instead received %s', dispatchProps); 68 | 69 | return dispatchProps; 70 | }; 71 | 72 | return function connectWrapper(Component) { 73 | var connectRegistry = {}; 74 | var getStateProps = function getStateProps(id) { 75 | return connectRegistry[id] ? connectRegistry[id].stateProps : null; 76 | }; 77 | var setStateProps = function setStateProps(id, stateProps) { 78 | return connectRegistry[id].stateProps = stateProps; 79 | }; 80 | var getDispatchProps = function getDispatchProps(id) { 81 | return connectRegistry[id] ? connectRegistry[id].dispatchProps : null; 82 | }; 83 | var setDispatchProps = function setDispatchProps(id, dispatchProps) { 84 | return connectRegistry[id].dispatchProps = dispatchProps; 85 | }; 86 | 87 | var ConnectedComponent = { 88 | propTypes: { 89 | store: { source: 'store' }, 90 | storeState: { source: 'storeState' } 91 | }, 92 | 93 | beforeMount: function beforeMount(_ref, elm, setState) { 94 | var id = _ref.id; 95 | var props = _ref.props; 96 | 97 | (0, _invariant2['default'])(props.store, '[deku-redux][connect] Could not find store. Did you use `storePlugin` on your deku tree?'); 98 | (0, _invariant2['default'])(props.store.getState && props.store.subscribe && props.store.subscribe, '[deku-redux][connect] Could not recognise store. Did you use `storePlugin` with a valid redux store?'); 99 | 100 | connectRegistry[id] = {}; 101 | setStateProps(id, computeStateProps(props)); 102 | setDispatchProps(id, computeDispatchProps(props)); 103 | }, 104 | 105 | shouldUpdate: function shouldUpdate(_ref2, nextProps) { 106 | var id = _ref2.id; 107 | var props = _ref2.props; 108 | 109 | var storeChanged = nextProps.storeState !== props.storeState; 110 | var propsChanged = !(0, _shallowEquals['default'])(nextProps, props); 111 | 112 | var computeNewStateProps = storeChanged || propsChanged && mappedStateUseProps; 113 | var computeNewDispatchProps = propsChanged && mappedDispatchUseProps; 114 | 115 | var statePropsChanged = false; 116 | var dispatchPropsChanged = false; 117 | 118 | if (computeNewStateProps) { 119 | var _stateProps = computeStateProps(props); 120 | statePropsChanged = !(0, _shallowEquals['default'])(_stateProps, getStateProps(id)); 121 | if (statePropsChanged) { 122 | setStateProps(id, _stateProps); 123 | } 124 | } 125 | 126 | if (computeNewDispatchProps) { 127 | var dispatchProps = computeDispatchProps(props); 128 | dispatchPropsChanged = !(0, _shallowEquals['default'])(dispatchProps, getDispatchProps(id)); 129 | if (dispatchPropsChanged) { 130 | setDispatchProps(id, stateProps); 131 | } 132 | } 133 | 134 | return propsChanged || statePropsChanged || dispatchPropsChanged; 135 | }, 136 | 137 | beforeUnmount: function beforeUnmount(_ref3) { 138 | var id = _ref3.id; 139 | 140 | connectRegistry[id] = undefined; 141 | }, 142 | 143 | render: function render(_ref4) { 144 | var id = _ref4.id; 145 | var props = _ref4.props; 146 | 147 | // TODO: use _.omit or similar 148 | var parentProps = Object.keys(props).filter(function (prop) { 149 | return prop !== 'store' || prop !== 'storeState'; 150 | }).reduce(function (acc, prop) { 151 | acc[prop] = props[prop]; 152 | return acc; 153 | }, {}); 154 | 155 | var componentProps = mergeProps(parentProps, getStateProps(id), getDispatchProps(id)); 156 | 157 | (0, _invariant2['default'])((0, _isPlainObject2['default'])(componentProps), '[deku-redux][connect] `mergeProps` function didn\'t return a plain object.'); 158 | 159 | // 160 | return { type: Component, children: props.children, attributes: componentProps }; 161 | } 162 | }; 163 | 164 | return ConnectedComponent; 165 | }; 166 | } 167 | 168 | module.exports = connect; 169 | }); --------------------------------------------------------------------------------