11import hoistStatics from 'hoist-non-react-statics'
2- import React , { useContext , useMemo , useRef , useReducer } from 'react'
2+ import React , {
3+ useContext ,
4+ useMemo ,
5+ useRef ,
6+ useReducer ,
7+ useLayoutEffect ,
8+ } from 'react'
39import { isValidElementType , isContextConsumer } from 'react-is'
4- import { createSubscription } from '../utils/Subscription'
10+ import type { Store } from 'redux'
11+ import type { SelectorFactory } from '../connect/selectorFactory'
12+ import { createSubscription , Subscription } from '../utils/Subscription'
513import { useIsomorphicLayoutEffect } from '../utils/useIsomorphicLayoutEffect'
614
7- import { ReactReduxContext } from './Context'
15+ import {
16+ ReactReduxContext ,
17+ ReactReduxContextValue ,
18+ ReactReduxContextInstance ,
19+ } from './Context'
820
921// Define some constant arrays just to avoid re-creating these
10- const EMPTY_ARRAY = [ ]
22+ const EMPTY_ARRAY : [ unknown , number ] = [ null , 0 ]
1123const NO_SUBSCRIPTION_ARRAY = [ null , null ]
1224
13- const stringifyComponent = ( Comp ) => {
25+ const stringifyComponent = ( Comp : unknown ) => {
1426 try {
1527 return JSON . stringify ( Comp )
1628 } catch ( err ) {
1729 return String ( Comp )
1830 }
1931}
2032
21- function storeStateUpdatesReducer ( state , action ) {
33+ function storeStateUpdatesReducer (
34+ state : [ payload : unknown , counter : number ] ,
35+ action : { payload : unknown }
36+ ) {
2237 const [ , updateCount ] = state
2338 return [ action . payload , updateCount + 1 ]
2439}
2540
41+ type EffectFunc = ( ...args : any [ ] ) => void | ReturnType < React . EffectCallback >
42+
2643function useIsomorphicLayoutEffectWithArgs (
27- effectFunc ,
28- effectArgs ,
29- dependencies
44+ effectFunc : EffectFunc ,
45+ effectArgs : any [ ] ,
46+ dependencies ?: React . DependencyList
3047) {
3148 useIsomorphicLayoutEffect ( ( ) => effectFunc ( ...effectArgs ) , dependencies )
3249}
3350
3451function captureWrapperProps (
35- lastWrapperProps ,
36- lastChildProps ,
37- renderIsScheduled ,
38- wrapperProps ,
39- actualChildProps ,
40- childPropsFromStoreUpdate ,
41- notifyNestedSubs
52+ lastWrapperProps : React . MutableRefObject < unknown > ,
53+ lastChildProps : React . MutableRefObject < unknown > ,
54+ renderIsScheduled : React . MutableRefObject < boolean > ,
55+ wrapperProps : React . MutableRefObject < unknown > ,
56+ actualChildProps : React . MutableRefObject < unknown > ,
57+ childPropsFromStoreUpdate : React . MutableRefObject < unknown > ,
58+ notifyNestedSubs : ( ) => void
4259) {
4360 // We want to capture the wrapper props and child props we used for later comparisons
4461 lastWrapperProps . current = wrapperProps
@@ -53,23 +70,23 @@ function captureWrapperProps(
5370}
5471
5572function subscribeUpdates (
56- shouldHandleStateChanges ,
57- store ,
58- subscription ,
59- childPropsSelector ,
60- lastWrapperProps ,
61- lastChildProps ,
62- renderIsScheduled ,
63- childPropsFromStoreUpdate ,
64- notifyNestedSubs ,
65- forceComponentUpdateDispatch
73+ shouldHandleStateChanges : boolean ,
74+ store : Store ,
75+ subscription : Subscription ,
76+ childPropsSelector : ( state : unknown , props : unknown ) => unknown ,
77+ lastWrapperProps : React . MutableRefObject < unknown > ,
78+ lastChildProps : React . MutableRefObject < unknown > ,
79+ renderIsScheduled : React . MutableRefObject < boolean > ,
80+ childPropsFromStoreUpdate : React . MutableRefObject < unknown > ,
81+ notifyNestedSubs : ( ) => void ,
82+ forceComponentUpdateDispatch : React . Dispatch < any >
6683) {
6784 // If we're not subscribed to the store, nothing to do here
6885 if ( ! shouldHandleStateChanges ) return
6986
7087 // Capture values for checking if and when this component unmounts
7188 let didUnsubscribe = false
72- let lastThrownError = null
89+ let lastThrownError : Error | null = null
7390
7491 // We'll run this callback every time a store subscription update propagates to this component
7592 const checkForUpdates = ( ) => {
@@ -148,7 +165,29 @@ function subscribeUpdates(
148165 return unsubscribeWrapper
149166}
150167
151- const initStateUpdates = ( ) => [ null , 0 ]
168+ const initStateUpdates = ( ) => EMPTY_ARRAY
169+
170+ export interface ConnectProps {
171+ reactReduxForwardedRef ?: React . ForwardedRef < unknown >
172+ context ?: ReactReduxContextInstance
173+ store ?: Store
174+ }
175+
176+ export type ConnectedComponent <
177+ C extends React . ComponentType < any > ,
178+ P
179+ > = React . NamedExoticComponent < JSX . LibraryManagedAttributes < C , P > > & {
180+ WrappedComponent : C
181+ }
182+
183+ interface ConnectAdvancedOptions {
184+ getDisplayName ?: ( name : string ) => string
185+ methodName ?: string
186+ shouldHandleStateChanges ?: boolean
187+ forwardRef ?: boolean
188+ context ?: typeof ReactReduxContext
189+ pure ?: boolean
190+ }
152191
153192export default function connectAdvanced (
154193 /*
@@ -168,7 +207,7 @@ export default function connectAdvanced(
168207 props. Do not use connectAdvanced directly without memoizing results between calls to your
169208 selector, otherwise the Connect component will re-render on every state or props change.
170209 */
171- selectorFactory ,
210+ selectorFactory : SelectorFactory < unknown , unknown , unknown , unknown > ,
172211 // options object:
173212 {
174213 // the func used to compute this HOC's displayName from the wrapped component's displayName.
@@ -179,19 +218,9 @@ export default function connectAdvanced(
179218 // probably overridden by wrapper functions such as connect()
180219 methodName = 'connectAdvanced' ,
181220
182- // REMOVED: if defined, the name of the property passed to the wrapped element indicating the number of
183- // calls to render. useful for watching in react devtools for unnecessary re-renders.
184- renderCountProp = undefined ,
185-
186221 // determines whether this HOC subscribes to store changes
187222 shouldHandleStateChanges = true ,
188223
189- // REMOVED: the key of props/context to get the store
190- storeKey = 'store' ,
191-
192- // REMOVED: expose the wrapped component via refs
193- withRef = false ,
194-
195224 // use React's forwardRef to expose a ref of the wrapped component
196225 forwardRef = false ,
197226
@@ -200,37 +229,13 @@ export default function connectAdvanced(
200229
201230 // additional options are passed through to the selectorFactory
202231 ...connectOptions
203- } = { }
232+ } : ConnectAdvancedOptions = { }
204233) {
205- if ( process . env . NODE_ENV !== 'production' ) {
206- if ( renderCountProp !== undefined ) {
207- throw new Error (
208- `renderCountProp is removed. render counting is built into the latest React Dev Tools profiling extension`
209- )
210- }
211- if ( withRef ) {
212- throw new Error (
213- 'withRef is removed. To access the wrapped instance, use a ref on the connected component'
214- )
215- }
216-
217- const customStoreWarningMessage =
218- 'To use a custom Redux store for specific components, create a custom React context with ' +
219- "React.createContext(), and pass the context object to React Redux's Provider and specific components" +
220- ' like: <Provider context={MyContext}><ConnectedComponent context={MyContext} /></Provider>. ' +
221- 'You may also pass a {context : MyContext} option to connect'
222-
223- if ( storeKey !== 'store' ) {
224- throw new Error (
225- 'storeKey has been removed and does not do anything. ' +
226- customStoreWarningMessage
227- )
228- }
229- }
230-
231234 const Context = context
232235
233- return function wrapWithConnect ( WrappedComponent ) {
236+ return function wrapWithConnect < WC extends React . ComponentType > (
237+ WrappedComponent : WC
238+ ) {
234239 if (
235240 process . env . NODE_ENV !== 'production' &&
236241 ! isValidElementType ( WrappedComponent )
@@ -252,43 +257,41 @@ export default function connectAdvanced(
252257 ...connectOptions ,
253258 getDisplayName,
254259 methodName,
255- renderCountProp,
256260 shouldHandleStateChanges,
257- storeKey,
258261 displayName,
259262 wrappedComponentName,
260263 WrappedComponent,
261264 }
262265
263266 const { pure } = connectOptions
264267
265- function createChildSelector ( store ) {
268+ function createChildSelector ( store : Store ) {
266269 return selectorFactory ( store . dispatch , selectorFactoryOptions )
267270 }
268271
269272 // If we aren't running in "pure" mode, we don't want to memoize values.
270273 // To avoid conditionally calling hooks, we fall back to a tiny wrapper
271274 // that just executes the given callback immediately.
272- const usePureOnlyMemo = pure ? useMemo : ( callback ) => callback ( )
273-
274- function ConnectFunction ( props ) {
275- const [
276- propsContext ,
277- reactReduxForwardedRef ,
278- wrapperProps ,
279- ] = useMemo ( ( ) => {
280- // Distinguish between actual "data" props that were passed to the wrapper component,
281- // and values needed to control behavior (forwarded refs, alternate context instances).
282- // To maintain the wrapperProps object reference, memoize this destructuring.
283- const { reactReduxForwardedRef, ...wrapperProps } = props
284- return [ props . context , reactReduxForwardedRef , wrapperProps ]
285- } , [ props ] )
286-
287- const ContextToUse = useMemo ( ( ) => {
275+ const usePureOnlyMemo = pure
276+ ? useMemo
277+ : ( callback : ( ) => void ) => callback ( )
278+
279+ function ConnectFunction < TOwnProps > ( props : ConnectProps & TOwnProps ) {
280+ const [ propsContext , reactReduxForwardedRef , wrapperProps ] =
281+ useMemo ( ( ) => {
282+ // Distinguish between actual "data" props that were passed to the wrapper component,
283+ // and values needed to control behavior (forwarded refs, alternate context instances).
284+ // To maintain the wrapperProps object reference, memoize this destructuring.
285+ const { reactReduxForwardedRef, ...wrapperProps } = props
286+ return [ props . context , reactReduxForwardedRef , wrapperProps ]
287+ } , [ props ] )
288+
289+ const ContextToUse : ReactReduxContextInstance = useMemo ( ( ) => {
288290 // Users may optionally pass in a custom context instance to use instead of our ReactReduxContext.
289291 // Memoize the check that determines which context instance we should use.
290292 return propsContext &&
291293 propsContext . Consumer &&
294+ // @ts -ignore
292295 isContextConsumer ( < propsContext . Consumer /> )
293296 ? propsContext
294297 : Context
@@ -302,10 +305,10 @@ export default function connectAdvanced(
302305 // This allows us to pass through a `store` prop that is just a plain value.
303306 const didStoreComeFromProps =
304307 Boolean ( props . store ) &&
305- Boolean ( props . store . getState ) &&
306- Boolean ( props . store . dispatch )
308+ Boolean ( props . store ! . getState ) &&
309+ Boolean ( props . store ! . dispatch )
307310 const didStoreComeFromContext =
308- Boolean ( contextValue ) && Boolean ( contextValue . store )
311+ Boolean ( contextValue ) && Boolean ( contextValue ! . store )
309312
310313 if (
311314 process . env . NODE_ENV !== 'production' &&
@@ -321,7 +324,9 @@ export default function connectAdvanced(
321324 }
322325
323326 // Based on the previous check, one of these must be true
324- const store = didStoreComeFromProps ? props . store : contextValue . store
327+ const store : Store = didStoreComeFromProps
328+ ? props . store !
329+ : contextValue ! . store
325330
326331 const childPropsSelector = useMemo ( ( ) => {
327332 // The child props selector needs the store reference as an input.
@@ -336,7 +341,7 @@ export default function connectAdvanced(
336341 // connected to the store via props shouldn't use subscription from context, or vice versa.
337342 const subscription = createSubscription (
338343 store ,
339- didStoreComeFromProps ? null : contextValue . subscription
344+ didStoreComeFromProps ? undefined : contextValue ! . subscription
340345 )
341346
342347 // `notifyNestedSubs` is duplicated to handle the case where the component is unmounted in
@@ -356,23 +361,26 @@ export default function connectAdvanced(
356361 // This component is directly subscribed to a store from props.
357362 // We don't want descendants reading from this store - pass down whatever
358363 // the existing context value is from the nearest connected ancestor.
359- return contextValue
364+ return contextValue !
360365 }
361366
362367 // Otherwise, put this component's subscription instance into context, so that
363368 // connected descendants won't update until after this component is done
364369 return {
365370 ...contextValue ,
366371 subscription,
367- }
372+ } as ReactReduxContextValue
368373 } , [ didStoreComeFromProps , contextValue , subscription ] )
369374
370375 // We need to force this wrapper component to re-render whenever a Redux store update
371376 // causes a change to the calculated child component props (or we caught an error in mapState)
372- const [
373- [ previousStateUpdateResult ] ,
374- forceComponentUpdateDispatch ,
375- ] = useReducer ( storeStateUpdatesReducer , EMPTY_ARRAY , initStateUpdates )
377+ const [ [ previousStateUpdateResult ] , forceComponentUpdateDispatch ] =
378+ useReducer (
379+ storeStateUpdatesReducer ,
380+ // @ts -ignore
381+ EMPTY_ARRAY as any ,
382+ initStateUpdates
383+ )
376384
377385 // Propagate any mapState/mapDispatch errors upwards
378386 if ( previousStateUpdateResult && previousStateUpdateResult . error ) {
@@ -441,6 +449,7 @@ export default function connectAdvanced(
441449 // We memoize the elements for the rendered child component as an optimization.
442450 const renderedWrappedComponent = useMemo (
443451 ( ) => (
452+ // @ts -ignore
444453 < WrappedComponent
445454 { ...actualChildProps }
446455 ref = { reactReduxForwardedRef }
@@ -470,19 +479,23 @@ export default function connectAdvanced(
470479 }
471480
472481 // If we're in "pure" mode, ensure our wrapper component only re-renders when incoming props have changed.
473- const Connect = pure ? React . memo ( ConnectFunction ) : ConnectFunction
482+ const _Connect = pure ? React . memo ( ConnectFunction ) : ConnectFunction
474483
484+ const Connect = _Connect as typeof _Connect & { WrappedComponent : WC }
475485 Connect . WrappedComponent = WrappedComponent
476486 Connect . displayName = ConnectFunction . displayName = displayName
477487
478488 if ( forwardRef ) {
479- const forwarded = React . forwardRef ( function forwardConnectRef (
489+ const _forwarded = React . forwardRef ( function forwardConnectRef (
480490 props ,
481491 ref
482492 ) {
483493 return < Connect { ...props } reactReduxForwardedRef = { ref } />
484494 } )
485495
496+ const forwarded = _forwarded as typeof _forwarded & {
497+ WrappedComponent : WC
498+ }
486499 forwarded . displayName = displayName
487500 forwarded . WrappedComponent = WrappedComponent
488501 return hoistStatics ( forwarded , WrappedComponent )
0 commit comments