Skip to content

Commit

Permalink
Merge pull request #2066 from reduxjs/feature/port-hoist-statics
Browse files Browse the repository at this point in the history
  • Loading branch information
markerikson committed Aug 26, 2023
2 parents bec3fa7 + c75af3a commit 5a406a9
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 26 deletions.
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,7 @@
}
},
"dependencies": {
"@types/hoist-non-react-statics": "^3.3.1",
"@types/use-sync-external-store": "^0.0.3",
"hoist-non-react-statics": "^3.3.2",
"react-is": "^18.0.0",
"use-sync-external-store": "^1.0.0"
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/connect.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* eslint-disable valid-jsdoc, @typescript-eslint/no-unused-vars */
import hoistStatics from 'hoist-non-react-statics'
import type { ComponentType } from 'react'
import * as React from 'react'
import { isValidElementType, isContextConsumer } from 'react-is'
Expand Down Expand Up @@ -31,6 +30,7 @@ import type { Subscription } from '../utils/Subscription'
import { createSubscription } from '../utils/Subscription'
import { useIsomorphicLayoutEffect } from '../utils/useIsomorphicLayoutEffect'
import shallowEqual from '../utils/shallowEqual'
import hoistStatics from '../utils/hoistStatics'
import warning from '../utils/warning'

import type {
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type {

import type { Action, UnknownAction, Dispatch } from 'redux'

import type { NonReactStatics } from 'hoist-non-react-statics'
import type { NonReactStatics } from './utils/hoistStatics'

import type { ConnectProps } from './components/connect'

Expand Down
136 changes: 136 additions & 0 deletions src/utils/hoistStatics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Copied directly from:
// https://github.com/mridgway/hoist-non-react-statics/blob/main/src/index.js
// https://unpkg.com/browse/@types/hoist-non-react-statics@3.3.1/index.d.ts

/**
* Copyright 2015, Yahoo! Inc.
* Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
*/
import type * as React from 'react'
import { ForwardRef, Memo, isMemo } from 'react-is'

const REACT_STATICS = {
childContextTypes: true,
contextType: true,
contextTypes: true,
defaultProps: true,
displayName: true,
getDefaultProps: true,
getDerivedStateFromError: true,
getDerivedStateFromProps: true,
mixins: true,
propTypes: true,
type: true,
} as const

const KNOWN_STATICS = {
name: true,
length: true,
prototype: true,
caller: true,
callee: true,
arguments: true,
arity: true,
} as const

const FORWARD_REF_STATICS = {
$$typeof: true,
render: true,
defaultProps: true,
displayName: true,
propTypes: true,
} as const

const MEMO_STATICS = {
$$typeof: true,
compare: true,
defaultProps: true,
displayName: true,
propTypes: true,
type: true,
} as const

const TYPE_STATICS = {
[ForwardRef]: FORWARD_REF_STATICS,
[Memo]: MEMO_STATICS,
} as const

function getStatics(component: any) {
// React v16.11 and below
if (isMemo(component)) {
return MEMO_STATICS
}

// React v16.12 and above
return TYPE_STATICS[component['$$typeof']] || REACT_STATICS
}

export type NonReactStatics<
S extends React.ComponentType<any>,
C extends {
[key: string]: true
} = {}
> = {
[key in Exclude<
keyof S,
S extends React.MemoExoticComponent<any>
? keyof typeof MEMO_STATICS | keyof C
: S extends React.ForwardRefExoticComponent<any>
? keyof typeof FORWARD_REF_STATICS | keyof C
: keyof typeof REACT_STATICS | keyof typeof KNOWN_STATICS | keyof C
>]: S[key]
}

const defineProperty = Object.defineProperty
const getOwnPropertyNames = Object.getOwnPropertyNames
const getOwnPropertySymbols = Object.getOwnPropertySymbols
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor
const getPrototypeOf = Object.getPrototypeOf
const objectPrototype = Object.prototype

export default function hoistNonReactStatics<
T extends React.ComponentType<any>,
S extends React.ComponentType<any>,
C extends {
[key: string]: true
} = {}
>(targetComponent: T, sourceComponent: S): T & NonReactStatics<S, C> {
if (typeof sourceComponent !== 'string') {
// don't hoist over string (html) components

if (objectPrototype) {
const inheritedComponent = getPrototypeOf(sourceComponent)
if (inheritedComponent && inheritedComponent !== objectPrototype) {
hoistNonReactStatics(targetComponent, inheritedComponent)
}
}

let keys: (string | symbol)[] = getOwnPropertyNames(sourceComponent)

if (getOwnPropertySymbols) {
keys = keys.concat(getOwnPropertySymbols(sourceComponent))
}

const targetStatics = getStatics(targetComponent)
const sourceStatics = getStatics(sourceComponent)

for (let i = 0; i < keys.length; ++i) {
const key = keys[i]
if (
!KNOWN_STATICS[key as keyof typeof KNOWN_STATICS] &&
!(sourceStatics && sourceStatics[key as keyof typeof sourceStatics]) &&
!(targetStatics && targetStatics[key as keyof typeof targetStatics])
) {
const descriptor = getOwnPropertyDescriptor(sourceComponent, key)
try {
// Avoid failures from read-only properties
defineProperty(targetComponent, key, descriptor!)
} catch (e) {
// ignore
}
}
}
}

return targetComponent as any
}
23 changes: 1 addition & 22 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3263,16 +3263,6 @@ __metadata:
languageName: node
linkType: hard

"@types/hoist-non-react-statics@npm:^3.3.1":
version: 3.3.1
resolution: "@types/hoist-non-react-statics@npm:3.3.1"
dependencies:
"@types/react": "*"
hoist-non-react-statics: ^3.3.0
checksum: 2c0778570d9a01d05afabc781b32163f28409bb98f7245c38d5eaf082416fdb73034003f5825eb5e21313044e8d2d9e1f3fe2831e345d3d1b1d20bcd12270719
languageName: node
linkType: hard

"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1":
version: 2.0.3
resolution: "@types/istanbul-lib-coverage@npm:2.0.3"
Expand Down Expand Up @@ -6588,15 +6578,6 @@ __metadata:
languageName: node
linkType: hard

"hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.2":
version: 3.3.2
resolution: "hoist-non-react-statics@npm:3.3.2"
dependencies:
react-is: ^16.7.0
checksum: b1538270429b13901ee586aa44f4cc3ecd8831c061d06cb8322e50ea17b3f5ce4d0e2e66394761e6c8e152cd8c34fb3b4b690116c6ce2bd45b18c746516cb9e8
languageName: node
linkType: hard

"hosted-git-info@npm:^2.1.4":
version: 2.8.9
resolution: "hosted-git-info@npm:2.8.9"
Expand Down Expand Up @@ -10025,7 +10006,7 @@ __metadata:
languageName: node
linkType: hard

"react-is@npm:^16.13.1, react-is@npm:^16.7.0, react-is@npm:^16.8.1, react-is@npm:^16.8.4":
"react-is@npm:^16.13.1, react-is@npm:^16.8.1, react-is@npm:^16.8.4":
version: 16.13.1
resolution: "react-is@npm:16.13.1"
checksum: f7a19ac3496de32ca9ae12aa030f00f14a3d45374f1ceca0af707c831b2a6098ef0d6bdae51bd437b0a306d7f01d4677fcc8de7c0d331eb47ad0f46130e53c5f
Expand Down Expand Up @@ -10126,7 +10107,6 @@ __metadata:
"@testing-library/react-12": "npm:@testing-library/react@^12"
"@testing-library/react-hooks": ^3.4.2
"@testing-library/react-native": ^7.1.0
"@types/hoist-non-react-statics": ^3.3.1
"@types/object-assign": ^4.0.30
"@types/react": ^18
"@types/react-dom": ^18
Expand All @@ -10145,7 +10125,6 @@ __metadata:
eslint-plugin-prettier: ^3.1.4
eslint-plugin-react: ^7.21.5
glob: ^7.1.6
hoist-non-react-statics: ^3.3.2
jest: ^29
jest-environment-jsdom: ^29.5.0
metro-react-native-babel-preset: ^0.76.6
Expand Down

0 comments on commit 5a406a9

Please sign in to comment.