Skip to content

Instantly share code, notes, and snippets.

@martin056
Last active April 15, 2020 11:33
Show Gist options
  • Save martin056/27b41f38cfc04eed8264cc9b66521ddf to your computer and use it in GitHub Desktop.
Save martin056/27b41f38cfc04eed8264cc9b66521ddf to your computer and use it in GitHub Desktop.
HOC that uses Redux connect and React-router withRouter
import React from 'react';
import _ from 'lodash';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { connect } from 'react-redux';
import { Diff } from 'utility-types';
import { AppState } from 'globalReducer';
import {
openDialog,
closeDialog,
registerDialogActions,
unregisterDialogActions,
IDialog,
TDialogActions
} from 'ducks/dialogs';
import { getUrlQueryWithDialog, getUrlQueryWithoutDialog } from 'shared/utils/dialogs';
import DialogNames from 'shared/dialogs/names';
export interface IWithDialogActions {
openDialog: (dialog: IDialog) => void;
closeDialog: (dialogName: DialogNames) => void;
registerDialogActions: (dialogName: DialogNames, actions: TDialogActions) => void;
}
const mapStateToProps = (state: AppState) => {
const { openedDialogs } = state.dialogs;
return { openedDialogs };
};
const mapDispatchToProps = {
openDialog,
closeDialog,
registerDialogActions,
unregisterDialogActions
};
type HOCProps = ReturnType<typeof mapStateToProps> &
typeof mapDispatchToProps &
RouteComponentProps;
function withDialogActions<T extends IWithDialogActions>(WrappedComponent: React.ComponentType<T>) {
class HOC extends React.Component<HOCProps> {
componentWillUnmount() {
_.map(this.props.openedDialogs, ({ name }) => this.props.unregisterDialogActions(name));
}
openDialog = (dialog: IDialog): void => {
this.props.openDialog(dialog);
if (dialog.withUrl) {
if (dialog.payload) {
throw new Error(
'Dialogs cannot have url and payload at the same time. Pass query instead?'
);
}
const urlQuery: string = getUrlQueryWithDialog(
this.props.location.search,
dialog.name,
dialog.query || {}
);
this.props.history.push({ search: urlQuery });
}
};
closeDialog = (dialogName: DialogNames): void => {
const dialog = _.find(this.props.openedDialogs, ({ name }) => name === dialogName);
if (dialog) {
this.props.closeDialog(dialogName);
if (dialog.withUrl) {
const urlQuery: string = getUrlQueryWithoutDialog(this.props.location.search);
this.props.history.push({ search: urlQuery });
}
}
};
registerDialogActions = (dialogName: DialogNames, actions: TDialogActions) =>
this.props.registerDialogActions(dialogName, actions);
render() {
const {
openDialog,
closeDialog,
registerDialogActions,
unregisterDialogActions,
history,
location,
match,
...rest
} = this.props;
return (
<WrappedComponent
// @ts-ignore. This is the correct type. Update the eslint version.
{...(rest as T)}
openDialog={this.openDialog}
closeDialog={this.closeDialog}
registerDialogActions={this.registerDialogActions}
/>
);
}
}
type StateProps = ReturnType<typeof mapStateToProps>;
type DispatchProps = typeof mapDispatchToProps;
type OwnProps = Diff<T, IWithDialogActions>;
return connect<StateProps, DispatchProps, OwnProps, AppState>(
mapStateToProps,
mapDispatchToProps
)(withRouter(HOC));
}
export default withDialogActions;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment