From cf6b7f36303374b029261d6153d9e20509f6153a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Mich=C3=83=C2=A1lek?= Date: Wed, 3 Jan 2018 11:42:43 +0100 Subject: [PATCH] This resolves issue #1: Create collection and withCollection HOCs to allow global reload --- README.md | 21 ++++++++++++++ src/collection.js | 74 +++++++++++++++++++++++++++++++++++++++++++++++ src/index.js | 1 + 3 files changed, 96 insertions(+) create mode 100644 src/collection.js diff --git a/README.md b/README.md index 4fa1f6c..5f3fab1 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,27 @@ const ConnectedComponent = reduxAutoloader({ })(ExampleComponent); ``` +#### Reload (refresh) all active loaders + +To create a "refresh all" button, simply use `collection` HOC instead of `reduxAutoloader` one. It has the same signature. + +```jsx +import { collection } from 'redux-autoloader'; + +const ConnectedComponent = collection(options)(ExampleComponent); +``` + +Then you can create a refresh button like using `withCollection` that passes a global `.refresh()` method as a prop. + +```jsx +import { withCollection } from 'redux-autoloader'; + +const ExampleComponent = ({ refresh }) => { + return ; +}; + +const ReloadComoponent = withCollection(ExampleComponent); +``` ## API Documentation diff --git a/src/collection.js b/src/collection.js new file mode 100644 index 0000000..9e4b310 --- /dev/null +++ b/src/collection.js @@ -0,0 +1,74 @@ +/* eslint-disable react/no-unused-prop-types, + react/prefer-stateless-function, + react/forbid-prop-types +*/ +import { PureComponent, createElement } from 'react'; +import PropTypes from 'prop-types'; +import getDisplayName from 'react-display-name'; +import reduxAutoloader from './reduxAutoloader'; + +import { assert } from './utils'; + +const hashTable = {}; + +export const collection = (options, mapStateToProps) => { + const name = options.name; + + assert(name, 'name is required'); + assert(typeof name === 'function' || typeof name === 'string', 'name must be a function or a string'); + + const getReducerName = typeof name === 'function' ? name : () => name; + + return (WrappedComponent) => { + class CollectionComponent extends PureComponent { + static propTypes = { + $refresh: PropTypes.func.isRequired, + $name: PropTypes.string.isRequired, + }; + + componentDidMount() { + hashTable[this.props.$name] = this.props.$refresh; + } + + componentWillReceiveProps(nextProps) { + if (nextProps.$refresh !== this.props.$refresh) { + if (this.props.$name !== nextProps.$name) { + delete hashTable[this.props.$name]; + } + + hashTable[nextProps.$name] = nextProps.$refresh; + } + } + + componentWillUnmount() { + delete hashTable[this.props.$name]; + } + + render() { + const { $name, $refresh, ...props } = this.props; // eslint-disable-line no-unused-vars + return createElement(WrappedComponent, props); + } + } + + CollectionComponent.displayName = `collection-${getDisplayName(WrappedComponent)}`; + CollectionComponent.WrappedComponent = WrappedComponent; + + return reduxAutoloader(options, (state, props) => ({ + $name: getReducerName(props), + $refresh: state.refresh, + ...mapStateToProps(state), + }))(CollectionComponent); + }; +}; + +const refresh = () => { + Object.values(hashTable).forEach(loader => loader()); +}; + +export const withCollection = (WrappedComponent) => { + const WithCollection = props => createElement(WrappedComponent, { ...props, refresh }); + + WithCollection.displayName = `withCollection-${getDisplayName(WrappedComponent)}`; + + return WithCollection; +}; diff --git a/src/index.js b/src/index.js index b8fced4..2df3753 100644 --- a/src/index.js +++ b/src/index.js @@ -3,3 +3,4 @@ export reducer from './reducer'; export * from './selectors'; export * from './actions'; export reduxAutoloader from './reduxAutoloader'; +export * from './collection';