From 9d1f50f5a63aa7291b296249b7db565c9e143225 Mon Sep 17 00:00:00 2001 From: gfgf-brain Date: Thu, 21 May 2026 03:33:21 -0700 Subject: [PATCH 1/3] Add solution for #91: Factory types for connect props --- brain_solution_91.md | 88 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 brain_solution_91.md diff --git a/brain_solution_91.md b/brain_solution_91.md new file mode 100644 index 0000000..0956e5c --- /dev/null +++ b/brain_solution_91.md @@ -0,0 +1,88 @@ +```markdown + + +# Factory Types for `connect` Props +===================================== + +In this section, we will explore how to use factory types with the `connect` function from `react-redux`. This allows for more control over the rendering performance and enables per-instance memoization. + +## Using `mapStateToPropsFactory` with TypeScript Generics + +When using `mapStateToPropsFactory`, we can return a function that takes `state` and `ownProps` as arguments. This function will be used as the `mapStateToProps` function for a particular component instance. + +```typescript +const mapStateToPropsFactory: MapStateToPropsFactory = () => { + // Example selector + const mySelector = makeMySelector(); + + return (state: State, ownProps: OwnProps) => { + return mySelector(state, ownProps); + } +} +``` + +However, when we return `any` or other types, we will get an error. To fix this, we need to use TypeScript generics to specify the types of `state` and `ownProps`. + +```typescript +const mapStateToPropsFactory: MapStateToPropsFactory = () => { + // Example selector + const mySelector = makeMySelector(); + + return (state: S, ownProps: O) => { + return mySelector(state, ownProps); + } +} +``` + +In this example, we use the `` syntax to specify the types of `state` and `ownProps`. This tells TypeScript that `state` will be of type `S` and `ownProps` will be of type `O`. + +## Using `mapDispatchToPropsFactory` with TypeScript Generics + +Similarly, we can use `mapDispatchToPropsFactory` with TypeScript generics to specify the types of `dispatch` and `ownProps`. + +```typescript +const mapDispatchToPropsFactory: MapDispatchToPropsFactory = () => { + // Example action creator + const myActionCreator = makeMyActionCreator(); + + return (dispatch: D, ownProps: O) => { + return myActionCreator(dispatch, ownProps); + } +} +``` + +In this example, we use the `` syntax to specify the types of `dispatch` and `ownProps`. This tells TypeScript that `dispatch` will be of type `D` and `ownProps` will be of type `O`. + +## Example Usage + +Here is an example of how to use `mapStateToPropsFactory` and `mapDispatchToPropsFactory` with TypeScript generics: + +```typescript +const mapStateToPropsFactory: MapStateToPropsFactory = () => { + // Example selector + const mySelector = makeMySelector(); + + return (state: S, ownProps: O) => { + return mySelector(state, ownProps); + } +} + +const mapDispatchToPropsFactory: MapDispatchToPropsFactory = () => { + // Example action creator + const myActionCreator = makeMyActionCreator(); + + return (dispatch: D, ownProps: O) => { + return myActionCreator(dispatch, ownProps); + } +} + +export default connect(mapStateToPropsFactory, mapDispatchToPropsFactory)(MyComponent); +``` + +In this example, we use `mapStateToPropsFactory` and `mapDispatchToPropsFactory` with TypeScript generics to specify the types of `state`, `ownProps`, `dispatch`, and `ownProps`. This allows us to use the `connect` function with more control over the rendering performance and enables per-instance memoization. + +## Conclusion + +In this section, we explored how to use factory types with the `connect` function from `react-redux` using TypeScript generics and type inference. We saw how to use `mapStateToPropsFactory` and `mapDispatchToPropsFactory` with TypeScript generics to specify the types of `state`, `ownProps`, `dispatch`, and `ownProps`. This allows us to use the `connect` function with more control over the rendering performance and enables per-instance memoization. +``` \ No newline at end of file From 14092ecccf84ad07c82d609053bc1630a1931ded Mon Sep 17 00:00:00 2001 From: gfgf-brain Date: Wed, 27 May 2026 20:56:31 -0700 Subject: [PATCH 2/3] Fix #91: Add Typing connect() Factory Functions section to README - MapStateToPropsFactory + MapDispatchToPropsFactory type signatures - Per-instance reselect memoization via factory pattern - Comparison: implicit vs explicit return type annotation - Full typed component with OwnProps + StateProps + DispatchProps - When-to-use table Closes #91 --- README.md | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/README.md b/README.md index 647b5c7..713c921 100644 --- a/README.md +++ b/README.md @@ -2247,6 +2247,144 @@ Higher-Order Components: --- +--- + +## Typing connect() Factory Functions + +React-Redux's `connect()` accepts a **factory function** form of `mapStateToProps` and `mapDispatchToProps`. When you return a function instead of an object, React-Redux calls it once per component instance — enabling per-instance memoization with `reselect`. + +The challenge is typing the factory correctly. React-Redux exports `MapStateToPropsFactory` and `MapDispatchToPropsFactory` for exactly this purpose. + +### MapStateToPropsFactory + +```typescript +import { connect, MapStateToPropsFactory } from 'react-redux'; +import { createSelector } from 'reselect'; + +interface OwnProps { + userId: string; +} + +interface StateProps { + username: string; + isActive: boolean; +} + +interface RootState { + users: Record; +} + +// Factory: called once per component instance. +// Returns the actual mapStateToProps for that instance. +const makeMapStateToProps: MapStateToPropsFactory = + () => { + // Each instance gets its own memoized selector — no cross-instance cache invalidation. + const selectUser = createSelector( + (state: RootState) => state.users, + (_: RootState, ownProps: OwnProps) => ownProps.userId, + (users, id) => users[id], + ); + + return (state, ownProps) => ({ + username: selectUser(state, ownProps)?.name ?? 'Unknown', + isActive: selectUser(state, ownProps)?.active ?? false, + }); + }; + +export default connect(makeMapStateToProps)(MyComponent); +``` + +### MapDispatchToPropsFactory + +```typescript +import { Dispatch } from 'redux'; +import { MapDispatchToPropsFactory } from 'react-redux'; +import { updateUser, deleteUser } from './actions'; + +interface DispatchProps { + onUpdate: (name: string) => void; + onDelete: () => void; +} + +const makeMapDispatchToProps: MapDispatchToPropsFactory = + () => (dispatch: Dispatch, ownProps: OwnProps) => ({ + onUpdate: (name) => dispatch(updateUser({ id: ownProps.userId, name })), + onDelete: () => dispatch(deleteUser({ id: ownProps.userId })), + }); + +export default connect(makeMapStateToProps, makeMapDispatchToProps)(MyComponent); +``` + +### Why Not Just Skip the Return Type? + +Omitting the return type annotation works at runtime but loses type safety: + +```typescript +// ❌ Works, but TypeScript cannot check the factory's return type +const makeMapState = () => (state: RootState, ownProps: OwnProps) => ({ + username: state.users[ownProps.userId]?.name, + // typo: `usrname` would silently pass as `string | undefined` without return type +}); + +// ✅ Explicit return type — errors caught at the factory definition, not at use-site +const makeMapState: MapStateToPropsFactory = () => + (state, ownProps) => ({ + username: state.users[ownProps.userId]?.name ?? 'Unknown', + isActive: state.users[ownProps.userId]?.active ?? false, + }); +``` + +### Full Typed Component + +```typescript +import React from 'react'; +import { connect, MapStateToPropsFactory } from 'react-redux'; +import { createSelector } from 'reselect'; + +// Own props passed to the component from outside +interface OwnProps { userId: string; } +// Props injected by mapStateToProps +interface StateProps { username: string; isActive: boolean; } +// Props injected by mapDispatchToProps +interface DispatchProps { onDelete: () => void; } + +type Props = OwnProps & StateProps & DispatchProps; + +const UserCard: React.FC = ({ username, isActive, onDelete }) => ( +
+ {username} + +
+); + +const makeMapState: MapStateToPropsFactory = () => { + const sel = createSelector( + (s: RootState) => s.users, + (_: RootState, p: OwnProps) => p.userId, + (users, id) => users[id], + ); + return (state, ownProps) => ({ + username: sel(state, ownProps)?.name ?? 'Unknown', + isActive: sel(state, ownProps)?.active ?? false, + }); +}; + +const mapDispatch = (dispatch: Dispatch, { userId }: OwnProps): DispatchProps => ({ + onDelete: () => dispatch(deleteUser({ id: userId })), +}); + +export default connect(makeMapState, mapDispatch)(UserCard); +``` + +### When to Use Factory Functions + +| Use case | Use factory? | +|---|---| +| Simple derived data, one instance on screen | No — plain `mapStateToProps` is fine | +| Same component rendered in a list (multiple instances) | **Yes** — separate selector cache per item | +| mapStateToProps that depends on `ownProps` in a selector | **Yes** — prevents cache thrashing | +| `mapDispatchToProps` that binds `ownProps` into action creators | Yes, or use `bindActionCreators` | + # Contributors Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)): From 4d6e4545a93520dca7881df6acf39885b8e1f3e8 Mon Sep 17 00:00:00 2001 From: gfgf-brain Date: Wed, 27 May 2026 21:13:09 -0700 Subject: [PATCH 3/3] Fix review feedback on #314: code quality, missing imports, remove scratchpad - Remove redundant --- separator - Cache selectUser/sel in const user (avoid double memoized selector call) - Add OwnProps interface to make snippet self-contained - Full Typed Component: add Dispatch, deleteUser, RootState imports - Delete brain_solution_91.md --- README.md | 33 ++++++++++++----- brain_solution_91.md | 88 -------------------------------------------- 2 files changed, 24 insertions(+), 97 deletions(-) delete mode 100644 brain_solution_91.md diff --git a/README.md b/README.md index 713c921..7e5cc5a 100644 --- a/README.md +++ b/README.md @@ -2247,7 +2247,6 @@ Higher-Order Components: --- ---- ## Typing connect() Factory Functions @@ -2285,10 +2284,13 @@ const makeMapStateToProps: MapStateToPropsFactory users[id], ); - return (state, ownProps) => ({ - username: selectUser(state, ownProps)?.name ?? 'Unknown', - isActive: selectUser(state, ownProps)?.active ?? false, - }); + return (state, ownProps) => { + const user = selectUser(state, ownProps); + return { + username: user?.name ?? 'Unknown', + isActive: user?.active ?? false, + }; + }; }; export default connect(makeMapStateToProps)(MyComponent); @@ -2301,6 +2303,10 @@ import { Dispatch } from 'redux'; import { MapDispatchToPropsFactory } from 'react-redux'; import { updateUser, deleteUser } from './actions'; +interface OwnProps { + userId: string; +} + interface DispatchProps { onUpdate: (name: string) => void; onDelete: () => void; @@ -2338,8 +2344,14 @@ const makeMapState: MapStateToPropsFactory = () ```typescript import React from 'react'; +import { Dispatch } from 'redux'; import { connect, MapStateToPropsFactory } from 'react-redux'; import { createSelector } from 'reselect'; +import { deleteUser } from './actions'; + +interface RootState { + users: Record; +} // Own props passed to the component from outside interface OwnProps { userId: string; } @@ -2363,10 +2375,13 @@ const makeMapState: MapStateToPropsFactory = () (_: RootState, p: OwnProps) => p.userId, (users, id) => users[id], ); - return (state, ownProps) => ({ - username: sel(state, ownProps)?.name ?? 'Unknown', - isActive: sel(state, ownProps)?.active ?? false, - }); + return (state, ownProps) => { + const user = sel(state, ownProps); + return { + username: user?.name ?? 'Unknown', + isActive: user?.active ?? false, + }; + }; }; const mapDispatch = (dispatch: Dispatch, { userId }: OwnProps): DispatchProps => ({ diff --git a/brain_solution_91.md b/brain_solution_91.md deleted file mode 100644 index 0956e5c..0000000 --- a/brain_solution_91.md +++ /dev/null @@ -1,88 +0,0 @@ -```markdown - - -# Factory Types for `connect` Props -===================================== - -In this section, we will explore how to use factory types with the `connect` function from `react-redux`. This allows for more control over the rendering performance and enables per-instance memoization. - -## Using `mapStateToPropsFactory` with TypeScript Generics - -When using `mapStateToPropsFactory`, we can return a function that takes `state` and `ownProps` as arguments. This function will be used as the `mapStateToProps` function for a particular component instance. - -```typescript -const mapStateToPropsFactory: MapStateToPropsFactory = () => { - // Example selector - const mySelector = makeMySelector(); - - return (state: State, ownProps: OwnProps) => { - return mySelector(state, ownProps); - } -} -``` - -However, when we return `any` or other types, we will get an error. To fix this, we need to use TypeScript generics to specify the types of `state` and `ownProps`. - -```typescript -const mapStateToPropsFactory: MapStateToPropsFactory = () => { - // Example selector - const mySelector = makeMySelector(); - - return (state: S, ownProps: O) => { - return mySelector(state, ownProps); - } -} -``` - -In this example, we use the `` syntax to specify the types of `state` and `ownProps`. This tells TypeScript that `state` will be of type `S` and `ownProps` will be of type `O`. - -## Using `mapDispatchToPropsFactory` with TypeScript Generics - -Similarly, we can use `mapDispatchToPropsFactory` with TypeScript generics to specify the types of `dispatch` and `ownProps`. - -```typescript -const mapDispatchToPropsFactory: MapDispatchToPropsFactory = () => { - // Example action creator - const myActionCreator = makeMyActionCreator(); - - return (dispatch: D, ownProps: O) => { - return myActionCreator(dispatch, ownProps); - } -} -``` - -In this example, we use the `` syntax to specify the types of `dispatch` and `ownProps`. This tells TypeScript that `dispatch` will be of type `D` and `ownProps` will be of type `O`. - -## Example Usage - -Here is an example of how to use `mapStateToPropsFactory` and `mapDispatchToPropsFactory` with TypeScript generics: - -```typescript -const mapStateToPropsFactory: MapStateToPropsFactory = () => { - // Example selector - const mySelector = makeMySelector(); - - return (state: S, ownProps: O) => { - return mySelector(state, ownProps); - } -} - -const mapDispatchToPropsFactory: MapDispatchToPropsFactory = () => { - // Example action creator - const myActionCreator = makeMyActionCreator(); - - return (dispatch: D, ownProps: O) => { - return myActionCreator(dispatch, ownProps); - } -} - -export default connect(mapStateToPropsFactory, mapDispatchToPropsFactory)(MyComponent); -``` - -In this example, we use `mapStateToPropsFactory` and `mapDispatchToPropsFactory` with TypeScript generics to specify the types of `state`, `ownProps`, `dispatch`, and `ownProps`. This allows us to use the `connect` function with more control over the rendering performance and enables per-instance memoization. - -## Conclusion - -In this section, we explored how to use factory types with the `connect` function from `react-redux` using TypeScript generics and type inference. We saw how to use `mapStateToPropsFactory` and `mapDispatchToPropsFactory` with TypeScript generics to specify the types of `state`, `ownProps`, `dispatch`, and `ownProps`. This allows us to use the `connect` function with more control over the rendering performance and enables per-instance memoization. -``` \ No newline at end of file