Skip to content

Commit 19ae75e

Browse files
authored
Merge pull request #1842 from reduxjs/feature/uses-compat-shim
2 parents 335133f + 87d1ec4 commit 19ae75e

File tree

14 files changed

+269
-331
lines changed

14 files changed

+269
-331
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
fail-fast: false
4141
matrix:
4242
node: ['14.x']
43-
ts: ['3.9', '4.0', '4.1', '4.2', '4.3', 'next']
43+
ts: ['4.0', '4.1', '4.2', '4.3', '4.4', '4.5', 'next']
4444
steps:
4545
- name: Checkout repo
4646
uses: actions/checkout@v2

jest.config.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ const rnConfig = {
2929
},
3030
}
3131

32+
const compatEntryConfig = {
33+
...tsStandardConfig,
34+
displayName: 'Compat',
35+
moduleNameMapper: {
36+
'^react$': 'react-17',
37+
'^react-dom$': 'react-dom-17',
38+
'^react-test-renderer$': 'react-test-renderer-17',
39+
'^@testing-library/react$': '@testing-library/react-12',
40+
'../../src/index': '<rootDir>/src/compat',
41+
},
42+
}
43+
3244
module.exports = {
33-
projects: [tsStandardConfig, rnConfig],
45+
projects: [tsStandardConfig, rnConfig, compatEntryConfig],
3446
}

package.json

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"coverage": "codecov"
4141
},
4242
"peerDependencies": {
43-
"react": "^18.0.0-alpha || ^18.0.0-beta"
43+
"react": "^18.0.0-beta"
4444
},
4545
"peerDependenciesMeta": {
4646
"react-dom": {
@@ -52,12 +52,15 @@
5252
},
5353
"dependencies": {
5454
"@babel/runtime": "^7.12.1",
55+
"@testing-library/react-12": "npm:@testing-library/react@^12",
5556
"@types/hoist-non-react-statics": "^3.3.1",
56-
"@types/use-sync-external-store": "^0.0.0",
57+
"@types/use-sync-external-store": "^0.0.3",
5758
"hoist-non-react-statics": "^3.3.2",
58-
"loose-envify": "^1.4.0",
59-
"react-is": "^16.13.1",
60-
"use-sync-external-store": "1.0.0-alpha-5cccacd13-20211101"
59+
"react-17": "npm:react@^17",
60+
"react-dom-17": "npm:react-dom@^17",
61+
"react-is": "^18.0.0-beta-fdc1d617a-20211118",
62+
"react-test-renderer-17": "npm:react-test-renderer@^17",
63+
"use-sync-external-store": "1.0.0-beta-fdc1d617a-20211118"
6164
},
6265
"devDependencies": {
6366
"@babel/cli": "^7.12.1",
@@ -80,10 +83,9 @@
8083
"@testing-library/react": "13.0.0-alpha.4",
8184
"@testing-library/react-hooks": "^3.4.2",
8285
"@testing-library/react-native": "^7.1.0",
83-
"@types/create-react-class": "^15.6.3",
8486
"@types/object-assign": "^4.0.30",
85-
"@types/react": "17.0.19",
86-
"@types/react-dom": "^17.0.9",
87+
"@types/react": "^17.0.35",
88+
"@types/react-dom": "^17.0.11",
8789
"@types/react-is": "^17.0.1",
8890
"@types/react-native": "^0.64.12",
8991
"@types/react-redux": "^7.1.18",
@@ -92,9 +94,7 @@
9294
"babel-eslint": "^10.1.0",
9395
"babel-jest": "^26.6.1",
9496
"codecov": "^3.8.0",
95-
"create-react-class": "^15.7.0",
9697
"cross-env": "^7.0.2",
97-
"es3ify": "^0.2.0",
9898
"eslint": "^7.12.0",
9999
"eslint-config-prettier": "^6.14.0",
100100
"eslint-plugin-import": "^2.22.1",
@@ -103,20 +103,15 @@
103103
"glob": "^7.1.6",
104104
"jest": "^26.6.1",
105105
"prettier": "^2.1.2",
106-
"react": "18.0.0-alpha-5cccacd13-20211101",
107-
"react-dom": "18.0.0-alpha-5cccacd13-20211101",
106+
"react": "18.0.0-beta-fdc1d617a-20211118",
107+
"react-dom": "18.0.0-beta-fdc1d617a-20211118",
108108
"react-native": "^0.64.1",
109-
"react-test-renderer": "18.0.0-alpha-5cccacd13-20211101",
109+
"react-test-renderer": "18.0.0-beta-fdc1d617a-20211118",
110110
"redux": "^4.0.5",
111111
"rimraf": "^3.0.2",
112112
"rollup": "^2.32.1",
113113
"rollup-plugin-terser": "^7.0.2",
114114
"ts-jest": "26.5.6",
115115
"typescript": "^4.3.4"
116-
},
117-
"browserify": {
118-
"transform": [
119-
"loose-envify"
120-
]
121116
}
122117
}

src/alternate-renderers.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
export * from './exports'
1+
// The "alternate renderers" entry point is primarily here to fall back on a no-op
2+
// version of `unstable_batchedUpdates`, for use with renderers other than ReactDOM/RN.
3+
// Examples include React-Three-Fiber, Ink, etc.
4+
// Because of that, we'll also assume the useSyncExternalStore compat shim is needed.
5+
6+
import { useSyncExternalStore } from 'use-sync-external-store/shim'
7+
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector'
8+
9+
import { setSyncFunctions } from './utils/useSyncExternalStore'
10+
11+
setSyncFunctions(useSyncExternalStore, useSyncExternalStoreWithSelector)
212

313
import { getBatch } from './utils/batch'
414

@@ -7,3 +17,5 @@ import { getBatch } from './utils/batch'
717
const batch = getBatch()
818

919
export { batch }
20+
21+
export * from './exports'

src/compat.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// The "compat" entry point assumes we're working with standard ReactDOM/RN, but
2+
// older versions that do not include `useSyncExternalStore` (React 16.9 - 17.x).
3+
// Because of that, the useSyncExternalStore compat shim is needed.
4+
5+
import { useSyncExternalStore } from 'use-sync-external-store/shim'
6+
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector'
7+
8+
import { setSyncFunctions } from './utils/useSyncExternalStore'
9+
import { unstable_batchedUpdates as batch } from './utils/reactBatchedUpdates'
10+
import { setBatch } from './utils/batch'
11+
12+
setSyncFunctions(useSyncExternalStore, useSyncExternalStoreWithSelector)
13+
14+
// Enable batched updates in our subscriptions for use
15+
// with standard React renderers (ReactDOM, React Native)
16+
setBatch(batch)
17+
18+
export { batch }
19+
20+
export * from './exports'

src/components/Context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react'
2-
import { Action, AnyAction, Store } from 'redux'
2+
import type { Action, AnyAction, Store } from 'redux'
33
import type { Subscription } from '../utils/Subscription'
44

55
export interface ReactReduxContextValue<

src/components/connect.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
/* eslint-disable valid-jsdoc, @typescript-eslint/no-unused-vars */
22
import hoistStatics from 'hoist-non-react-statics'
3-
import React, {
4-
useContext,
5-
useMemo,
6-
useRef,
7-
useReducer,
8-
// @ts-ignore
9-
useSyncExternalStore,
10-
} from 'react'
3+
import React, { useContext, useMemo, useRef, useReducer } from 'react'
114
import { isValidElementType, isContextConsumer } from 'react-is'
125

136
import type { Store, Dispatch, Action, AnyAction } from 'redux'
@@ -35,6 +28,7 @@ import defaultMergePropsFactories from '../connect/mergeProps'
3528

3629
import { createSubscription, Subscription } from '../utils/Subscription'
3730
import { useIsomorphicLayoutEffect } from '../utils/useIsomorphicLayoutEffect'
31+
import { getSyncFunctions } from '../utils/useSyncExternalStore'
3832
import shallowEqual from '../utils/shallowEqual'
3933

4034
import {
@@ -43,6 +37,8 @@ import {
4337
ReactReduxContextInstance,
4438
} from './Context'
4539

40+
const [useSyncExternalStore] = getSyncFunctions()
41+
4642
// Define some constant arrays just to avoid re-creating these
4743
const EMPTY_ARRAY: [unknown, number] = [null, 0]
4844
const NO_SUBSCRIPTION_ARRAY = [null, null]

src/hooks/useSelector.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { useContext, useDebugValue } from 'react'
22

3-
// @ts-ignore
4-
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector'
5-
63
import { useReduxContext as useDefaultReduxContext } from './useReduxContext'
74
import { ReactReduxContext } from '../components/Context'
8-
import { DefaultRootState, EqualityFn } from '../types'
5+
import { getSyncFunctions } from '../utils/useSyncExternalStore'
6+
import type { DefaultRootState, EqualityFn } from '../types'
7+
8+
const [, useSyncExternalStoreWithSelector] = getSyncFunctions()
99

1010
const refEquality: EqualityFn<any> = (a, b) => a === b
1111

src/index.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
1-
export * from './exports'
1+
// The default entry point assumes we are working with React 18, and thus have
2+
// useSyncExternalStore available. We can import that directly from React itself.
3+
// The useSyncExternalStoreWithSelector has to be imported, but we can use the
4+
// non-shim version. This shaves off the byte size of the shim.
5+
6+
// @ts-ignore React types not updated yet
7+
import { useSyncExternalStore } from 'react'
8+
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector'
29

10+
import { setSyncFunctions } from './utils/useSyncExternalStore'
311
import { unstable_batchedUpdates as batch } from './utils/reactBatchedUpdates'
412
import { setBatch } from './utils/batch'
513

14+
setSyncFunctions(useSyncExternalStore, useSyncExternalStoreWithSelector)
15+
616
// Enable batched updates in our subscriptions for use
717
// with standard React renderers (ReactDOM, React Native)
818
setBatch(batch)
919

1020
export { batch }
21+
22+
export * from './exports'

src/utils/useSyncExternalStore.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { useSyncExternalStore } from 'use-sync-external-store'
2+
import type { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector'
3+
4+
const notInitialized = () => {
5+
throw new Error('Not initialize!')
6+
}
7+
8+
let uSES: typeof useSyncExternalStore = notInitialized
9+
let uSESWS: typeof useSyncExternalStoreWithSelector = notInitialized
10+
11+
// Allow injecting the actual functions from the entry points
12+
export const setSyncFunctions = (
13+
sync: typeof useSyncExternalStore,
14+
withSelector: typeof useSyncExternalStoreWithSelector
15+
) => {
16+
uSES = sync
17+
uSESWS = withSelector
18+
}
19+
20+
// Supply a getter just to skip dealing with ESM bindings
21+
export const getSyncFunctions = () => [uSES, uSESWS] as const

0 commit comments

Comments
 (0)