|
| 1 | +/* eslint-disable react/no-unused-prop-types, |
| 2 | + react/prefer-stateless-function, |
| 3 | + react/forbid-prop-types |
| 4 | +*/ |
| 5 | +import { PureComponent, createElement } from 'react'; |
| 6 | +import PropTypes from 'prop-types'; |
| 7 | +import getDisplayName from 'react-display-name'; |
| 8 | +import reduxAutoloader from './reduxAutoloader'; |
| 9 | + |
| 10 | +import { assert } from './utils'; |
| 11 | + |
| 12 | +const hashTable = {}; |
| 13 | + |
| 14 | +export const collection = (options, mapStateToProps) => { |
| 15 | + const name = options.name; |
| 16 | + |
| 17 | + assert(name, 'name is required'); |
| 18 | + assert(typeof name === 'function' || typeof name === 'string', 'name must be a function or a string'); |
| 19 | + |
| 20 | + const getReducerName = typeof name === 'function' ? name : () => name; |
| 21 | + |
| 22 | + return (WrappedComponent) => { |
| 23 | + class CollectionComponent extends PureComponent { |
| 24 | + static propTypes = { |
| 25 | + $refresh: PropTypes.func.isRequired, |
| 26 | + $name: PropTypes.string.isRequired, |
| 27 | + }; |
| 28 | + |
| 29 | + componentDidMount() { |
| 30 | + hashTable[this.props.$name] = this.props.$refresh; |
| 31 | + } |
| 32 | + |
| 33 | + componentWillReceiveProps(nextProps) { |
| 34 | + if (nextProps.$refresh !== this.props.$refresh) { |
| 35 | + if (this.props.$name !== nextProps.$name) { |
| 36 | + delete hashTable[this.props.$name]; |
| 37 | + } |
| 38 | + |
| 39 | + hashTable[nextProps.$name] = nextProps.$refresh; |
| 40 | + } |
| 41 | + } |
| 42 | + |
| 43 | + componentWillUnmount() { |
| 44 | + delete hashTable[this.props.$name]; |
| 45 | + } |
| 46 | + |
| 47 | + render() { |
| 48 | + const { $name, $refresh, ...props } = this.props; // eslint-disable-line no-unused-vars |
| 49 | + return createElement(WrappedComponent, props); |
| 50 | + } |
| 51 | + } |
| 52 | + |
| 53 | + CollectionComponent.displayName = `collection-${getDisplayName(WrappedComponent)}`; |
| 54 | + CollectionComponent.WrappedComponent = WrappedComponent; |
| 55 | + |
| 56 | + return reduxAutoloader(options, (state, props) => ({ |
| 57 | + $name: getReducerName(props), |
| 58 | + $refresh: state.refresh, |
| 59 | + ...mapStateToProps(state), |
| 60 | + }))(CollectionComponent); |
| 61 | + }; |
| 62 | +}; |
| 63 | + |
| 64 | +const refresh = () => { |
| 65 | + Object.values(hashTable).forEach(loader => loader()); |
| 66 | +}; |
| 67 | + |
| 68 | +export const withCollection = (WrappedComponent) => { |
| 69 | + const WithCollection = props => createElement(WrappedComponent, { ...props, refresh }); |
| 70 | + |
| 71 | + WithCollection.displayName = `withCollection-${getDisplayName(WrappedComponent)}`; |
| 72 | + |
| 73 | + return WithCollection; |
| 74 | +}; |
0 commit comments