Skip to content

Commit

Permalink
Merge pull request #81 from Terreii/react-18
Browse files Browse the repository at this point in the history
Support React 18
  • Loading branch information
Terreii authored Jun 17, 2022
2 parents ac23575 + 663afd1 commit 5fbc396
Show file tree
Hide file tree
Showing 19 changed files with 11,150 additions and 12,493 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14.x'
node-version: '16.x'
- name: Lint
run: |
npm ci
Expand All @@ -31,7 +31,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14.x'
node-version: '16.x'
- name: Release to GitHub Pages
run: |
git config --global user.email "[email protected]"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:

strategy:
matrix:
node-version: [12.x, 14.x, 16.x]
node-version: [12.x, 14.x, 16.x, 18.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
Expand All @@ -40,7 +40,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14.x'
node-version: '16.x'
cache: 'npm'
- run: npm ci
- name: Release
Expand Down
14,172 changes: 6,210 additions & 7,962 deletions package-lock.json

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@
"@babel/preset-env": "^7.12.1",
"@babel/preset-react": "^7.12.1",
"@babel/preset-typescript": "^7.12.1",
"@testing-library/react-hooks": "^7.0.1",
"@testing-library/react": "^13.3.0",
"@tsconfig/node12": "^1.0.9",
"@types/jest": "^26.0.15",
"@types/node": "^12.20.16",
"@types/node": "^12.20.55",
"@types/pouchdb-adapter-memory": "^6.1.3",
"@typescript-eslint/eslint-plugin": "^4.6.0",
"@typescript-eslint/parser": "^4.6.0",
Expand All @@ -66,9 +66,10 @@
"pouchdb-selector-core": "^7.2.2",
"prettier": "^2.1.2",
"pretty-quick": "^3.1.0",
"react": "^17.0.1",
"react-test-renderer": "^17.0.1",
"semantic-release": "^17.2.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-test-renderer": "^18.2.0",
"semantic-release": "^19.0.3",
"textlint": "^12.0.2",
"textlint-rule-alex": "^3.0.0",
"textlint-rule-common-misspellings": "^1.0.1",
Expand All @@ -80,7 +81,7 @@
"@types/pouchdb-core": "^7.0.9",
"@types/pouchdb-find": "^6.3.6",
"@types/pouchdb-mapreduce": "^6.1.6",
"@types/react": "^17.0.14",
"@types/react": "^18.0.14",
"fast-deep-equal": "^3.1.3",
"pouchdb-core": "^7.3.0",
"pouchdb-errors": "^7.3.0",
Expand Down
137 changes: 59 additions & 78 deletions src/context.test.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
import { renderHook } from '@testing-library/react-hooks'
import React from 'react'
import { renderHook } from '@testing-library/react'
import React, { StrictMode } from 'react'
import PouchDB from 'pouchdb-core'
import memory from 'pouchdb-adapter-memory'

import { Provider, useContext } from './context'

PouchDB.plugin(memory)

test('should throw an error if there is no pouchdb context', () => {
const { result } = renderHook(() => useContext())

expect(result.error).toBeInstanceOf(Error)
expect(result.error.message).toBe(
'could not find PouchDB context value; please ensure the component is wrapped in a <Provider>'
)
})

test('should render a Provider which provide the passed pouchdb database', async () => {
const myPouch = new PouchDB('test', { adapter: 'memory' })

const { result } = renderHook(() => useContext(), {
wrapper: function Wrapper({ children }: { children: React.ReactChildren }) {
return <Provider pouchdb={myPouch}>{children}</Provider>
wrapper: function Wrapper({ children }) {
return (
<StrictMode>
<Provider pouchdb={myPouch}>{children}</Provider>
</StrictMode>
)
},
})

Expand All @@ -42,8 +37,12 @@ test('should unsubscribe all when the database changes', async () => {
let db = myPouch

const { result, rerender } = renderHook(() => useContext(), {
wrapper: function Wrapper({ children }: { children: React.ReactChildren }) {
return <Provider pouchdb={db}>{children}</Provider>
wrapper: function Wrapper({ children }) {
return (
<StrictMode>
<Provider pouchdb={db}>{children}</Provider>
</StrictMode>
)
},
})

Expand All @@ -68,8 +67,12 @@ test('should unsubscribe all when a database gets destroyed', async () => {
const myPouch = new PouchDB('test', { adapter: 'memory' })

const { result } = renderHook(() => useContext(), {
wrapper: function Wrapper({ children }: { children: React.ReactChildren }) {
return <Provider pouchdb={myPouch}>{children}</Provider>
wrapper: function Wrapper({ children }) {
return (
<StrictMode>
<Provider pouchdb={myPouch}>{children}</Provider>
</StrictMode>
)
},
})

Expand All @@ -85,56 +88,24 @@ test('should unsubscribe all when a database gets destroyed', async () => {
expect(unsubscribe).toHaveBeenCalled()
})

test('should throw an error if a wrong name is passed to useContext', async () => {
const myPouch = new PouchDB('test', { adapter: 'memory' })

const { result, rerender } = renderHook((name?: string) => useContext(name), {
initialProps: undefined,
wrapper: function Wrapper({ children }: { children: React.ReactChildren }) {
return <Provider pouchdb={myPouch}>{children}</Provider>
},
})

expect(result.error).toBeUndefined()

rerender('not-existing')

expect(result.error).toBeInstanceOf(Error)
expect(result.error.message).toBe(
'could not find a PouchDB database with name of "not-existing"'
)

rerender('_default')

expect(result.error).toBeUndefined()

rerender('test')

expect(result.error).toBeUndefined()

await myPouch.destroy()
})

test('should use the optional name argument', async () => {
const myPouch = new PouchDB('test', { adapter: 'memory' })

const { result, rerender } = renderHook((name: string) => useContext(name), {
const { result } = renderHook((name: string) => useContext(name), {
initialProps: 'other',
wrapper: function Wrapper({ children }: { children: React.ReactChildren }) {
wrapper: function Wrapper({ children }) {
return (
<Provider pouchdb={myPouch} name="other">
{children}
</Provider>
<StrictMode>
<Provider pouchdb={myPouch} name="other">
{children}
</Provider>
</StrictMode>
)
},
})

expect(result.current.pouchdb).toBe(myPouch)

rerender('test')

expect(result.error).toBeInstanceOf(Error)

await myPouch.destroy()
})

Expand All @@ -144,11 +115,13 @@ test('should render a Provider that gives access to multiple databases', async (

const { result, rerender } = renderHook((name?: string) => useContext(name), {
initialProps: undefined,
wrapper: function Wrapper({ children }: { children: React.ReactChildren }) {
wrapper: function Wrapper({ children }) {
return (
<Provider databases={{ myPouch, other }} default="myPouch">
{children}
</Provider>
<StrictMode>
<Provider databases={{ myPouch, other }} default="myPouch">
{children}
</Provider>
</StrictMode>
)
},
})
Expand Down Expand Up @@ -179,11 +152,13 @@ test('should combine a parent context into its context', async () => {

const { result, rerender } = renderHook((name?: string) => useContext(name), {
initialProps: undefined,
wrapper: function Wrapper({ children }: { children: React.ReactChildren }) {
wrapper: function Wrapper({ children }) {
return (
<Provider pouchdb={parent}>
<Provider pouchdb={child}>{children}</Provider>
</Provider>
<StrictMode>
<Provider pouchdb={parent}>
<Provider pouchdb={child}>{children}</Provider>
</Provider>
</StrictMode>
)
},
})
Expand Down Expand Up @@ -216,13 +191,15 @@ test('should combine a parent context into its context if the child is multi db'

const { result, rerender } = renderHook((name?: string) => useContext(name), {
initialProps: undefined,
wrapper: function Wrapper({ children }: { children: React.ReactChildren }) {
wrapper: function Wrapper({ children }) {
return (
<Provider pouchdb={parent}>
<Provider databases={{ other: child }} default="other">
{children}
<StrictMode>
<Provider pouchdb={parent}>
<Provider databases={{ other: child }} default="other">
{children}
</Provider>
</Provider>
</Provider>
</StrictMode>
)
},
})
Expand Down Expand Up @@ -255,13 +232,15 @@ test('should allow the use of a parent context database name in default', async

const { result } = renderHook((name?: string) => useContext(name), {
initialProps: undefined,
wrapper: function Wrapper({ children }: { children: React.ReactChildren }) {
wrapper: function Wrapper({ children }) {
return (
<Provider pouchdb={parent}>
<Provider databases={{ other: child }} default="test">
{children}
<StrictMode>
<Provider pouchdb={parent}>
<Provider databases={{ other: child }} default="test">
{children}
</Provider>
</Provider>
</Provider>
</StrictMode>
)
},
})
Expand All @@ -278,11 +257,13 @@ test('should handle a database name of default', async () => {

const { result, rerender } = renderHook((name?: string) => useContext(name), {
initialProps: undefined,
wrapper: function Wrapper({ children }: { children: React.ReactChildren }) {
wrapper: function Wrapper({ children }) {
return (
<Provider databases={{ default: myPouch, other }} default="other">
{children}
</Provider>
<StrictMode>
<Provider databases={{ default: myPouch, other }} default="other">
{children}
</Provider>
</StrictMode>
)
},
})
Expand Down
49 changes: 22 additions & 27 deletions src/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import React, {
createContext,
useContext as useReactContext,
useMemo,
useEffect,
useRef,
useState,
ReactNode,
} from 'react'

Expand Down Expand Up @@ -110,31 +109,25 @@ function useAddSubscriptionManager(databases: {
[key: string]: PouchDB.Database
}): ContextObject {
// memory for last DB and SubscriptionManager pairs
const lastContextObject = useRef<ContextObject>({})
const [lastDatabases, setLastDatabases] = useState(databases)
const [lastContextObject, setLastContextObject] =
useState<ContextObject | null>(null)

useEffect(
() => () => {
// unsubscribe all SubscriptionManager when the component un-mounts
for (const pair of Object.values(lastContextObject.current)) {
pair.subscriptionManager.unsubscribeAll()
}
},
[]
)

// Keys of last lastContextObject
// All databases that didn't change will be reused and their keys deleted from this Set.
// All keys left, the database did change and the SubscriptionManager will be unsubscribed.
const lastKeys = new Set(Object.keys(lastContextObject.current))
// This is for re-renders, which happens when setState is called while rendering.
// https://beta.reactjs.org/apis/usestate#storing-information-from-previous-renders
if (lastContextObject && databases === lastDatabases) return lastContextObject

const contextObjects: ContextObject = {}
const dbToUnsubscribe = new Set(Object.keys(lastContextObject ?? {}))
let didAddNewDatabase = false

for (const [key, db] of Object.entries(databases)) {
if (lastKeys.has(key) && db === lastContextObject.current[key].pouchdb) {
contextObjects[key] = lastContextObject.current[key]
lastKeys.delete(key)
if (lastContextObject && lastDatabases[key] === db) {
// DB didn't change
contextObjects[key] = lastContextObject[key]
dbToUnsubscribe.delete(key)
} else {
// It is a new or changed DB
didAddNewDatabase = true
contextObjects[key] = {
pouchdb: db,
Expand All @@ -143,17 +136,19 @@ function useAddSubscriptionManager(databases: {
}
}

// no database was created and no database got removed, or did change --> use the old one
if (!didAddNewDatabase && lastKeys.size === 0) {
return lastContextObject.current
if (didAddNewDatabase || dbToUnsubscribe.size > 0) {
setLastDatabases(databases)
setLastContextObject(contextObjects)
} else if (lastContextObject) {
return lastContextObject // nothing did change and not first render: use last
}

// unsubscribe all SubscriptionManagers who's database did change/got removed.
for (const key of lastKeys) {
lastContextObject.current[key].subscriptionManager.unsubscribeAll()
if (lastContextObject) {
for (const key of dbToUnsubscribe) {
lastContextObject[key].subscriptionManager.unsubscribeAll()
}
}

lastContextObject.current = contextObjects
return contextObjects
}

Expand Down
Loading

0 comments on commit 5fbc396

Please sign in to comment.