import shallowEqual from 'shallowequal'; import { inject, createVNode, watchEffect, toRaw } from 'vue'; import omit from 'omit.js'; import { getOptionProps } from '../props-util'; function getDisplayName(WrappedComponent) { return WrappedComponent.name || 'Component'; } const defaultMapStateToProps = () => ({}); export default function connect(mapStateToProps) { const shouldSubscribe = !!mapStateToProps; const finalMapStateToProps = mapStateToProps || defaultMapStateToProps; return function wrapWithConnect(WrappedComponent) { const tempProps = omit(WrappedComponent.props || {}, ['store']); const props = {}; Object.keys(tempProps).forEach(k => { props[k] = { ...tempProps[k], required: false }; }); const Connect = { name: `Connect_${getDisplayName(WrappedComponent)}`, inheritAttrs: false, props, setup() { return { storeContext: inject('storeContext', {}), }; }, data() { this.store = this.storeContext.store; this.preProps = getOptionProps(this); watchEffect(() => { if (mapStateToProps && mapStateToProps.length === 2) { this.subscribed = finalMapStateToProps(this.store.getState(), this.$props); } }); return { subscribed: finalMapStateToProps(this.store.getState(), this.$props), }; }, mounted() { this.trySubscribe(); }, beforeUnmount() { this.tryUnsubscribe(); }, methods: { handleChange() { if (!this.unsubscribe) { return; } const props = getOptionProps(this); const nextSubscribed = finalMapStateToProps(this.store.getState(), props); if ( !shallowEqual(this.preProps, props) || !shallowEqual(toRaw(this.subscribed), nextSubscribed) ) { this.subscribed = nextSubscribed; } }, trySubscribe() { if (shouldSubscribe) { this.unsubscribe = this.store.subscribe(this.handleChange); this.handleChange(); } }, tryUnsubscribe() { if (this.unsubscribe) { this.unsubscribe(); this.unsubscribe = null; } }, getWrappedInstance() { return this.$refs.wrappedInstance; }, }, render() { const { $slots = {}, subscribed, store, $attrs } = this; const props = getOptionProps(this); this.preProps = { ...props }; const wrapProps = { ...props, ...subscribed, ...$attrs, store, ref: 'wrappedInstance', }; const slots = {}; for (let [key, value] of Object.entries($slots)) { slots[key] = () => value(); } return createVNode(WrappedComponent, wrapProps, slots); }, }; return Connect; }; }