diff --git a/README.md b/README.md index 647b5c7..95d5031 100644 --- a/README.md +++ b/README.md @@ -1816,6 +1816,51 @@ const mapDispatchToProps = (dispatch: Dispatch) => ``` +If you need to connect a **generic** component, prefer a small factory and infer +Redux props through `ConnectedProps`: + +```tsx +import * as React from 'react'; +import { connect, ConnectedProps } from 'react-redux'; +import * as Types from 'MyTypes'; + +type GenericListProps = { + items: ReadonlyArray; + renderItem: (item: T) => React.ReactNode; + getItemKey: (item: T) => React.Key; +}; + +export const createConnectedGenericList = () => { + type OwnProps = GenericListProps & { + selectedKey?: React.Key; + }; + + const mapStateToProps = (state: Types.RootState, ownProps: OwnProps) => ({ + selectedKey: ownProps.selectedKey ?? state.counters.reduxCounter, + }); + + const connector = connect(mapStateToProps); + type ReduxProps = ConnectedProps; + type Props = GenericListProps & ReduxProps; + + const GenericList: React.FC = ({ items, renderItem, getItemKey, selectedKey }) => ( +
    + {items.map(item => ( +
  • + {renderItem(item)} +
  • + ))} +
+ ); + + return connector(GenericList); +}; + +// Create once at module scope, not inside render, to avoid remounting +// and extra connector work. +const ConnectedNumberList = createConnectedGenericList(); +``` + [⇧ back to top](#table-of-contents) ### Typing `useSelector` and `useDispatch` diff --git a/README_SOURCE.md b/README_SOURCE.md index 358fcc3..b0f3937 100644 --- a/README_SOURCE.md +++ b/README_SOURCE.md @@ -710,6 +710,51 @@ const mapDispatchToProps = (dispatch: Dispatch) => ``` +If you need to connect a **generic** component, prefer a small factory and infer +Redux props through `ConnectedProps`: + +```tsx +import * as React from 'react'; +import { connect, ConnectedProps } from 'react-redux'; +import * as Types from 'MyTypes'; + +type GenericListProps = { + items: ReadonlyArray; + renderItem: (item: T) => React.ReactNode; + getItemKey: (item: T) => React.Key; +}; + +export const createConnectedGenericList = () => { + type OwnProps = GenericListProps & { + selectedKey?: React.Key; + }; + + const mapStateToProps = (state: Types.RootState, ownProps: OwnProps) => ({ + selectedKey: ownProps.selectedKey ?? state.counters.reduxCounter, + }); + + const connector = connect(mapStateToProps); + type ReduxProps = ConnectedProps; + type Props = GenericListProps & ReduxProps; + + const GenericList: React.FC = ({ items, renderItem, getItemKey, selectedKey }) => ( +
    + {items.map(item => ( +
  • + {renderItem(item)} +
  • + ))} +
+ ); + + return connector(GenericList); +}; + +// Create once at module scope, not inside render, to avoid remounting +// and extra connector work. +const ConnectedNumberList = createConnectedGenericList(); +``` + [⇧ back to top](#table-of-contents) ### Typing `useSelector` and `useDispatch`