From d339e61c254cc095ec94e74ad4df0355c19674b3 Mon Sep 17 00:00:00 2001 From: ncthuc2004 Date: Thu, 14 May 2026 12:25:35 +0700 Subject: [PATCH 1/3] docs: add connected generic component pattern for connect --- README.md | 41 +++++++++++++++++++++++++++++++++++++++++ README_SOURCE.md | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/README.md b/README.md index 647b5c7..0337758 100644 --- a/README.md +++ b/README.md @@ -1816,6 +1816,47 @@ 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 OwnProps = { + selectedId?: string; +}; + +type GenericListProps = { + items: ReadonlyArray; + renderItem: (item: T) => React.ReactNode; +}; + +const mapStateToProps = (state: Types.RootState, ownProps: OwnProps) => ({ + selectedId: ownProps.selectedId ?? String(state.counters.reduxCounter), +}); + +const connector = connect(mapStateToProps); +type ReduxProps = ConnectedProps; + +export const createConnectedGenericList = () => { + type Props = GenericListProps & ReduxProps; + + const GenericList: React.FC = ({ items, renderItem, selectedId }) => ( +
    + {items.map((item, index) => ( +
  • + {renderItem(item)} +
  • + ))} +
+ ); + + return connector(GenericList); +}; +``` + [⇧ back to top](#table-of-contents) ### Typing `useSelector` and `useDispatch` diff --git a/README_SOURCE.md b/README_SOURCE.md index 358fcc3..779242d 100644 --- a/README_SOURCE.md +++ b/README_SOURCE.md @@ -710,6 +710,47 @@ 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 OwnProps = { + selectedId?: string; +}; + +type GenericListProps = { + items: ReadonlyArray; + renderItem: (item: T) => React.ReactNode; +}; + +const mapStateToProps = (state: Types.RootState, ownProps: OwnProps) => ({ + selectedId: ownProps.selectedId ?? String(state.counters.reduxCounter), +}); + +const connector = connect(mapStateToProps); +type ReduxProps = ConnectedProps; + +export const createConnectedGenericList = () => { + type Props = GenericListProps & ReduxProps; + + const GenericList: React.FC = ({ items, renderItem, selectedId }) => ( +
    + {items.map((item, index) => ( +
  • + {renderItem(item)} +
  • + ))} +
+ ); + + return connector(GenericList); +}; +``` + [⇧ back to top](#table-of-contents) ### Typing `useSelector` and `useDispatch` From 0f4b0fc0b7322aebf2ba818a0718af902592158d Mon Sep 17 00:00:00 2001 From: ncthuc2004 Date: Thu, 14 May 2026 14:15:49 +0700 Subject: [PATCH 2/3] docs: avoid index keys and document generic list factory usage --- README.md | 9 +++++++-- README_SOURCE.md | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0337758..cbaf079 100644 --- a/README.md +++ b/README.md @@ -1831,6 +1831,7 @@ type OwnProps = { type GenericListProps = { items: ReadonlyArray; renderItem: (item: T) => React.ReactNode; + getItemKey: (item: T) => React.Key; }; const mapStateToProps = (state: Types.RootState, ownProps: OwnProps) => ({ @@ -1843,10 +1844,10 @@ type ReduxProps = ConnectedProps; export const createConnectedGenericList = () => { type Props = GenericListProps & ReduxProps; - const GenericList: React.FC = ({ items, renderItem, selectedId }) => ( + const GenericList: React.FC = ({ items, renderItem, getItemKey, selectedId }) => (
    {items.map((item, index) => ( -
  • +
  • {renderItem(item)}
  • ))} @@ -1855,6 +1856,10 @@ export const createConnectedGenericList = () => { 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) diff --git a/README_SOURCE.md b/README_SOURCE.md index 779242d..b674c72 100644 --- a/README_SOURCE.md +++ b/README_SOURCE.md @@ -725,6 +725,7 @@ type OwnProps = { type GenericListProps = { items: ReadonlyArray; renderItem: (item: T) => React.ReactNode; + getItemKey: (item: T) => React.Key; }; const mapStateToProps = (state: Types.RootState, ownProps: OwnProps) => ({ @@ -737,10 +738,10 @@ type ReduxProps = ConnectedProps; export const createConnectedGenericList = () => { type Props = GenericListProps & ReduxProps; - const GenericList: React.FC = ({ items, renderItem, selectedId }) => ( + const GenericList: React.FC = ({ items, renderItem, getItemKey, selectedId }) => (
      {items.map((item, index) => ( -
    • +
    • {renderItem(item)}
    • ))} @@ -749,6 +750,10 @@ export const createConnectedGenericList = () => { 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) From 49a5e57df1ac6091d9874e421f2834bd292b3457 Mon Sep 17 00:00:00 2001 From: ncthuc2004 Date: Sun, 17 May 2026 00:43:00 +0700 Subject: [PATCH 3/3] docs(connect): make generic connected example compile-safe --- README.md | 25 ++++++++++++------------- README_SOURCE.md | 25 ++++++++++++------------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index cbaf079..95d5031 100644 --- a/README.md +++ b/README.md @@ -1824,30 +1824,29 @@ import * as React from 'react'; import { connect, ConnectedProps } from 'react-redux'; import * as Types from 'MyTypes'; -type OwnProps = { - selectedId?: string; -}; - type GenericListProps = { items: ReadonlyArray; renderItem: (item: T) => React.ReactNode; getItemKey: (item: T) => React.Key; }; -const mapStateToProps = (state: Types.RootState, ownProps: OwnProps) => ({ - selectedId: ownProps.selectedId ?? String(state.counters.reduxCounter), -}); +export const createConnectedGenericList = () => { + type OwnProps = GenericListProps & { + selectedKey?: React.Key; + }; -const connector = connect(mapStateToProps); -type ReduxProps = ConnectedProps; + const mapStateToProps = (state: Types.RootState, ownProps: OwnProps) => ({ + selectedKey: ownProps.selectedKey ?? state.counters.reduxCounter, + }); -export const createConnectedGenericList = () => { + const connector = connect(mapStateToProps); + type ReduxProps = ConnectedProps; type Props = GenericListProps & ReduxProps; - const GenericList: React.FC = ({ items, renderItem, getItemKey, selectedId }) => ( + const GenericList: React.FC = ({ items, renderItem, getItemKey, selectedKey }) => (
        - {items.map((item, index) => ( -
      • + {items.map(item => ( +
      • {renderItem(item)}
      • ))} diff --git a/README_SOURCE.md b/README_SOURCE.md index b674c72..b0f3937 100644 --- a/README_SOURCE.md +++ b/README_SOURCE.md @@ -718,30 +718,29 @@ import * as React from 'react'; import { connect, ConnectedProps } from 'react-redux'; import * as Types from 'MyTypes'; -type OwnProps = { - selectedId?: string; -}; - type GenericListProps = { items: ReadonlyArray; renderItem: (item: T) => React.ReactNode; getItemKey: (item: T) => React.Key; }; -const mapStateToProps = (state: Types.RootState, ownProps: OwnProps) => ({ - selectedId: ownProps.selectedId ?? String(state.counters.reduxCounter), -}); +export const createConnectedGenericList = () => { + type OwnProps = GenericListProps & { + selectedKey?: React.Key; + }; -const connector = connect(mapStateToProps); -type ReduxProps = ConnectedProps; + const mapStateToProps = (state: Types.RootState, ownProps: OwnProps) => ({ + selectedKey: ownProps.selectedKey ?? state.counters.reduxCounter, + }); -export const createConnectedGenericList = () => { + const connector = connect(mapStateToProps); + type ReduxProps = ConnectedProps; type Props = GenericListProps & ReduxProps; - const GenericList: React.FC = ({ items, renderItem, getItemKey, selectedId }) => ( + const GenericList: React.FC = ({ items, renderItem, getItemKey, selectedKey }) => (
          - {items.map((item, index) => ( -
        • + {items.map(item => ( +
        • {renderItem(item)}
        • ))}