Skip to content

Added mergeProps function to connect helper #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,3 @@ npm-debug.log
Session.vim
.netrwhist
*~

lib
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ function mapActionToProps(dispatch) {
data: { todo }
})
}
}
};
}

export default connect(mapStateToProps, mapActionToProps)(App);
Expand Down Expand Up @@ -141,3 +141,10 @@ const mapDispatchToProps = (dispatch) => ({})

export default connect(mapStateToProps, mapDispatchToProps)(Comp)
```

## connect([mapStateToProps], [mapDispatchToProps], [mergeProps])
Connects a Vue component to a Redux store.
### Arguments
* [mapStateToProps(state, [ownAttrs]): stateProps] (__Function__) Subscribes component to Redux store updates. This means that any time the store is updated, mapStateToProps will be called. The results of `mapStateToProps` must be a plain object, which will be merged into the component’s props.
* [mapDispatchToProps(dispatch): dispatchProps] (__Function__) Result must be a plain object
* [mergeProps(stateProps, dispatchProps): props] (__Function__) If specified, it is passed the result of `mapStateToProps()` and `mapDispatchToProps()`. The plain object you return from it will be passed as props to the wrapped component.
144 changes: 144 additions & 0 deletions lib/connect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
'use strict';

Object.defineProperty(exports, "__esModule", {
value: true
});

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; };

exports.default = connect;

var _normalizeProps = require('./normalizeProps');

var _normalizeProps2 = _interopRequireDefault(_normalizeProps);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function noop() {}

function getStore(component) {
return component.$store;
}

function getAttrs(component) {
var attrs = component._self.$options._parentVnode.data.attrs;
if (!attrs) {
return attrs;
}
// Convert props from kebab-case to camelCase notation
return Object.keys(attrs).reduce(function (memo, key) {
return _extends({}, memo, _defineProperty({}, key.replace(/[-](.)/g, function (match, group) {
return group.toUpperCase();
}), attrs[key]));
}, {});
}

function getStates(component, mapStateToProps) {
var store = getStore(component);
var attrs = getAttrs(component);

return mapStateToProps(store.getState(), attrs) || {};
}

function getActions(component, mapActionsToProps) {
var store = getStore(component);

return mapActionsToProps(store.dispatch, getAttrs.bind(null, component)) || {};
}

function getProps(component) {
var props = {};
var attrs = getAttrs(component);
var propNames = component.vuaReduxPropNames;

for (var ii = 0; ii < propNames.length; ii++) {
props[propNames[ii]] = component[propNames[ii]];
}

return _extends({}, props, attrs);
}

function getSlots(component) {
return Object.keys(component.$slots).reduce(function (memo, name) {
return _extends({}, memo, _defineProperty({}, name, function () {
return component.$slots[name];
}));
}, {});
}

function defaultMergeProps(stateProps, actionsProps) {
return _extends({}, stateProps, actionsProps);
}

/**
* 1. utilities are moved above because vue stores methods, states and props
* in the same namespace
* 2. actions are set while created
*/

/**
* @param mapStateToProps
* @param mapActionsToProps
* @param mergeProps
* @returns Object
*/
function connect(mapStateToProps, mapActionsToProps, mergeProps) {
mapStateToProps = mapStateToProps || noop;
mapActionsToProps = mapActionsToProps || noop;
mergeProps = mergeProps || defaultMergeProps;

return function (children) {

/** @namespace children.collect */
if (children.collect) {
children.props = _extends({}, (0, _normalizeProps2.default)(children.props || {}), (0, _normalizeProps2.default)(children.collect || {}));

var msg = 'vua-redux: collect is deprecated, use props ' + ('in ' + (children.name || 'anonymous') + ' component');

console.warn(msg);
}

return {
name: 'ConnectVuaRedux-' + (children.name || 'children'),

render: function render(h) {
return h(children, {
props: getProps(this),
scopedSlots: getSlots(this)
});
},
data: function data() {
var state = getStates(this, mapStateToProps);
var actions = getActions(this, mapActionsToProps);
var merged = mergeProps(state, actions);
var propNames = Object.keys(merged);

return _extends({}, merged, {
vuaReduxPropNames: propNames
});
},
created: function created() {
var _this = this;

var store = getStore(this);

this.vuaReduxUnsubscribe = store.subscribe(function () {
var state = getStates(_this, mapStateToProps);
var actions = getActions(_this, mapActionsToProps);
var merged = mergeProps(state, actions);
var propNames = Object.keys(merged);
_this.vuaReduxPropNames = propNames;

for (var ii = 0; ii < propNames.length; ii++) {
_this[propNames[ii]] = merged[propNames[ii]];
}
});
},
beforeDestroy: function beforeDestroy() {
this.vuaReduxUnsubscribe();
}
};
};
}
19 changes: 19 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use strict';

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.reduxStorePlugin = exports.connect = undefined;

var _connect2 = require('./connect');

var _connect3 = _interopRequireDefault(_connect2);

var _reduxStorePlugin2 = require('./reduxStorePlugin');

var _reduxStorePlugin3 = _interopRequireDefault(_reduxStorePlugin2);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

exports.connect = _connect3.default;
exports.reduxStorePlugin = _reduxStorePlugin3.default;
48 changes: 48 additions & 0 deletions lib/normalizeProps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict';

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = normalizeProps;

var _isArray = require('lodash/isArray');

var _isArray2 = _interopRequireDefault(_isArray);

var _isPlainObject = require('lodash/isPlainObject');

var _isPlainObject2 = _interopRequireDefault(_isPlainObject);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// https://github.com/vuejs/vue/blob/dev/src/util/options.js
function normalizeProps(props) {
var i,
val,
normalizedProps = {};

if ((0, _isArray2.default)(props)) {
i = props.length;
while (i--) {
val = props[i];
if (typeof val === 'string') {
normalizedProps[val] = null;
} else if (val.name) {
normalizedProps[val.name] = val;
}
}
} else if ((0, _isPlainObject2.default)(props)) {
var keys = Object.keys(props);
i = keys.length;
while (i--) {
var key = keys[i];
val = props[key];
normalizedProps[key] = props[key];
if (typeof val === 'function') {
normalizedProps[key] = { type: val };
}
}
}

return normalizedProps;
}
22 changes: 22 additions & 0 deletions lib/normalizeProps.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

var _expect = require('expect');

var _expect2 = _interopRequireDefault(_expect);

var _normalizeProps = require('./normalizeProps');

var _normalizeProps2 = _interopRequireDefault(_normalizeProps);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

describe('normalize props', function () {
it('should normalize array props', function () {
(0, _expect2.default)((0, _normalizeProps2.default)(['a', 'b'])).toEqual({ a: null, b: null });
});

it('should normalize object props', function () {
var props = { 'a': { type: String }, 'b': null };
(0, _expect2.default)((0, _normalizeProps2.default)(props)).toEqual(props);
});
});
21 changes: 21 additions & 0 deletions lib/reduxStorePlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = reduxStorePlugin;
function reduxStorePlugin(Vue) {
var storeId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'store';

Vue.mixin({
beforeCreate: function beforeCreate() {
var options = this.$options;
// store injection
if (options[storeId]) {
this.$store = options.store;
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store;
}
}
});
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "redux-vue",
"version": "0.7.1",
"version": "0.8.1",
"description": "Vue Redux binding higher order component",
"author": "Nadim Tuhin",
"repository": {
Expand Down
Loading