Skip to content

Instantly share code, notes, and snippets.

@troch
Created November 12, 2015 12:11
Show Gist options
  • Save troch/520eb25dce170a69758a to your computer and use it in GitHub Desktop.
Save troch/520eb25dce170a69758a to your computer and use it in GitHub Desktop.
Deku connect
import element from 'virtual-element';
import { bindActionCreators } from 'redux';
import shallowEquals from 'is-equal-shallow';
import isPlainObject from 'is-plain-object';
const defaultMapStateToProps = () => ({})
const defaultMapDispatchToProps = dispatch => ({ dispatch })
const defaultMergeProps = (stateProps, dispatchProps, parentProps) => ({
...parentProps,
...stateProps,
...dispatchProps
});
const wrapActionCreators = actionCreators => dispatch => bindActionCreators(actionCreators, dispatch);
function connect(mapStateToProps = defaultMapStateToProps, mapDispatchToProps = defaultMapDispatchToProps, mergeProps = defaultMergeProps) {
if(isPlainObject(mapDispatchToProps)) {
mapDispatchToProps = wrapActionCreators(mapDispatchToProps);
}
const mappedStateUseProps = mapStateToProps.length > 1;
const mappedDispatchUseProps = mapDispatchToProps.length > 1;
const computeStateProps = props => {
const state = props.store.getState();
const stateProps = mapStateToProps(state, props);
// TODO: invariant(plainObject)
return stateProps;
};
const computeDispatchProps = props => {
const dispatch = props.store.dispatch;
const dispatchProps = mapDispatchToProps(dispatch, props);
// TODO: invariant(plainObject)
return dispatchProps;
};
return function connectWrapper(Component) {
const connectRegistry = {};
const getStateProps = id => connectRegistry[id] ? connectRegistry[id].stateProps : null;
const setStateProps = (id, stateProps) => connectRegistry[id].stateProps = stateProps;
const getDispatchProps = id => connectRegistry[id] ? connectRegistry[id].dispatchProps : null;
const setDispatchProps = (id, dispatchProps) => connectRegistry[id].dispatchProps = dispatchProps;
const ConnectedComponent = {
propTypes: {
store: { source: 'store' },
storeState: { source: 'storeState' }
},
afterMount({ id, props }, elm, setState) {
// TODO: invariant store
connectRegistry{id} = {};
setStateProps(id, computeStateProps(props));
setDispatchProps(id, computeDispatchProps(props));
},
shouldUpdate({ id, props }, nextProps) {
const storeChanged = nextState.storeState !== state.storeState;
const propsChanged = !shallowEquals(nextProps, props);
const computeNewStateProps = storeChanged || (propsChanged && mappedStateUseProps);
const computeNewDispatchProps = propsChanged && mappedDispatchUseProps;
let statePropsChanged = false;
let dispatchPropsChanged = false;
if (computeNewStateProps) {
const stateProps = computeStateProps(props);
statePropsChanged = !shallowEquals(stateProps, getStateProps(id));
if (statePropsChanged) {
setStateProps(id, stateProps);
}
}
if (computeNewDispatchProps) {
const dispatchProps = computeDispatchProps(props);
dispatchPropsChanged = !shallowEquals(dispatchProps, getDispatchProps(id));
if (dispatchPropsChanged) {
setDispatchProps(id, stateProps);
}
}
return propsChanged || statePropsChanged || dispatchPropsChanged;
},
beforeUnmount({ id }) {
connectRegistry{id} = undefined;
},
render({ id, props }) {
// TODO: use _.omit or similar
const parentProps = Object.keys(props)
.filter(prop => prop !== 'store' || prop !== 'storeState');
.reduce((acc, prop) => {
acc[prop] = props[prop];
return acc;
}, {});
return element(Component, { ...parentProps, ...getStateProps(id), ...getDispatchProps(id) });
}
};
return ConnectedComponent;
};
}
export default connect;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment