Skip to content

Commit

Permalink
feat(broadcastQueryClient): experimental support for tab/window synci…
Browse files Browse the repository at this point in the history
…ng (TanStack#1793)

* feat(subscriptions): add event types and metadata to subscriptions

* feat(broadcastQueryClient): experimental support for tab/window syncing

* better types, update tests

* feat(broadcastQueryClient): experimental support for tab/window syncing

* rebase and update

* use transactions

* update docs

* Update broadcastQueryClient.md

* Update index.ts
  • Loading branch information
tannerlinsley authored Feb 23, 2021
1 parent 9fad892 commit 4bbfe2a
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 4 deletions.
6 changes: 6 additions & 0 deletions broadcastQueryClient-experimental/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"internal": true,
"main": "../lib/broadcastQueryClient-experimental/index.js",
"module": "../es/broadcastQueryClient-experimental/index.js",
"types": "../types/broadcastQueryClient-experimental/index.d.ts"
}
59 changes: 59 additions & 0 deletions docs/src/pages/plugins/broadcastQueryClient.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
id: broadcastQueryClient
title: broadcastQueryClient (Experimental)
---

> VERY IMPORTANT: This utility is currently in an experimental stage. This means that breaking changes will happen in minor AND patch releases. Use at your own risk. If you choose to rely on this in production in an experimental stage, please lock your version to a patch-level version to avoid unexpected breakages.
`broadcastQueryClient` is a utility for broadcasting and syncing the state of your queryClient between browser tabs/windows with the same origin.

## Installation

This utility comes packaged with `react-query` and is available under the `react-query/broadcastQueryClient-experimental` import.

## Usage

Import the `broadcastQueryClient` function, and pass it your `QueryClient` instance, and optionally, set a `broadcastChannel`.

```ts
import { broadcastQueryClient } from 'react-query/broadcastQueryClient-experimental'

const queryClient = new QueryClient()

broadcastQueryClient({
queryClient,
broadcastChannel: 'my-app',
})
```

## API

### `broadcastQueryClient`

Pass this function a `QueryClient` instance and optionally, a `broadcastChannel`.

```ts
broadcastQueryClient({ queryClient, broadcastChannel })
```

### `Options`

An object of options:

```ts
interface broadcastQueryClient {
/** The QueryClient to sync */
queryClient: QueryClient
/** This is the unique channel name that will be used
* to communicate between tabs and windows */
broadcastChannel?: string
}
```

The default options are:

```ts
{
broadcastChannel = 'react-query',
}
```
1 change: 1 addition & 0 deletions examples/basic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
},
"dependencies": {
"axios": "^0.21.1",
"broadcast-channel": "^3.4.1",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-query": "^3.5.0",
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,15 @@
"devtools",
"persistQueryClient-experimental",
"createLocalStoragePersistor-experimental",
"broadcastQueryClient-experimental",
"lib",
"react",
"scripts",
"types"
],
"dependencies": {
"@babel/runtime": "^7.5.5",
"broadcast-channel": "^3.4.1",
"match-sorter": "^6.0.2"
},
"peerDependencies": {
Expand Down
5 changes: 5 additions & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ const inputSrcs = [
'ReactQueryCreateLocalStoragePersistorExperimental',
'createLocalStoragePersistor-experimental',
],
[
'src/broadcastQueryClient-experimental/index.ts',
'ReactQueryBroadcastQueryClientExperimental',
'broadcastQueryClient-experimental',
],
]

const extensions = ['.js', '.jsx', '.es6', '.es', '.mjs', '.ts', '.tsx']
Expand Down
89 changes: 89 additions & 0 deletions src/broadcastQueryClient-experimental/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { BroadcastChannel } from 'broadcast-channel'
import { QueryClient } from '../core'

interface BroadcastQueryClientOptions {
queryClient: QueryClient
broadcastChannel: string
}

export function broadcastQueryClient({
queryClient,
broadcastChannel = 'react-query',
}: BroadcastQueryClientOptions) {
let transaction = false
const tx = (cb: () => void) => {
transaction = true
cb()
transaction = false
}

const channel = new BroadcastChannel(broadcastChannel, {
webWorkerSupport: false,
})

const queryCache = queryClient.getQueryCache()

queryClient.getQueryCache().subscribe(queryEvent => {
if (transaction || !queryEvent?.query) {
return
}

const {
query: { queryHash, queryKey, state },
} = queryEvent

if (
queryEvent.type === 'queryUpdated' &&
queryEvent.action?.type === 'success'
) {
channel.postMessage({
type: 'queryUpdated',
queryHash,
queryKey,
state,
})
}

if (queryEvent.type === 'queryRemoved') {
channel.postMessage({
type: 'queryRemoved',
queryHash,
queryKey,
})
}
})

channel.onmessage = action => {
if (!action?.type) {
return
}

tx(() => {
const { type, queryHash, queryKey, state } = action

if (type === 'queryUpdated') {
const query = queryCache.get(queryHash)

if (query) {
query.setState(state)
return
}

queryCache.build(
queryClient,
{
queryKey,
queryHash,
},
state
)
} else if (type === 'queryRemoved') {
const query = queryCache.get(queryHash)

if (query) {
queryCache.remove(query)
}
}
})
}
}
12 changes: 10 additions & 2 deletions src/core/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ interface ContinueAction {
interface SetStateAction<TData, TError> {
type: 'setState'
state: QueryState<TData, TError>
setStateOptions?: SetStateOptions
}

export type Action<TData, TError> =
Expand All @@ -118,6 +119,10 @@ export type Action<TData, TError> =
| SetStateAction<TData, TError>
| SuccessAction<TData>

export interface SetStateOptions {
meta?: any
}

// CLASS

export class Query<
Expand Down Expand Up @@ -216,8 +221,11 @@ export class Query<
return data
}

setState(state: QueryState<TData, TError>): void {
this.dispatch({ type: 'setState', state })
setState(
state: QueryState<TData, TError>,
setStateOptions?: SetStateOptions
): void {
this.dispatch({ type: 'setState', state, setStateOptions })
}

cancel(options?: CancelOptions): Promise<void> {
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.types.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"./src/hydration/index.ts",
"./src/devtools/index.ts",
"./src/persistQueryClient-experimental/index.ts",
"./src/createLocalStoragePersistor-experimental/index.ts"
"./src/createLocalStoragePersistor-experimental/index.ts",
"./src/broadcastQueryClient-experimental/index.ts"
],
"exclude": ["./src/**/*"]
}
57 changes: 56 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1944,6 +1944,13 @@
dependencies:
regenerator-runtime "^0.13.4"

"@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.13.tgz#0a21452352b02542db0ffb928ac2d3ca7cb6d66d"
integrity sha512-8+3UMPBrjFa/6TtKi/7sehPKqfAm4g6K+YQjyyFOLUTxzOngcRZTlAVY8sc2CORJYqdHQY8gRPHmn+qo15rCBw==
dependencies:
regenerator-runtime "^0.13.4"

"@babel/runtime@^7.8.4":
version "7.10.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839"
Expand Down Expand Up @@ -3088,6 +3095,11 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"

big-integer@^1.6.16:
version "1.6.48"
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==

binary-extensions@^1.0.0:
version "1.13.1"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
Expand Down Expand Up @@ -3136,6 +3148,19 @@ braces@^3.0.1:
dependencies:
fill-range "^7.0.1"

broadcast-channel@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.4.1.tgz#65b63068d0a5216026a19905c9b2d5e9adf0928a"
integrity sha512-VXYivSkuBeQY+pL5hNQQNvBdKKQINBAROm4G8lAbWQfOZ7Yn4TMcgLNlJyEqlkxy5G8JJBsI3VJ1u8FUTOROcg==
dependencies:
"@babel/runtime" "^7.7.2"
detect-node "^2.0.4"
js-sha3 "0.8.0"
microseconds "0.2.0"
nano-time "1.0.0"
rimraf "3.0.2"
unload "2.2.0"

brotli-size@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/brotli-size/-/brotli-size-4.0.0.tgz#a05ee3faad3c0e700a2f2da826ba6b4d76e69e5e"
Expand Down Expand Up @@ -3652,6 +3677,11 @@ detect-newline@^3.0.0:
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==

detect-node@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==

diff-sequences@^25.2.6:
version "25.2.6"
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd"
Expand Down Expand Up @@ -5468,6 +5498,11 @@ jest@^26.0.1:
import-local "^3.0.2"
jest-cli "^26.0.1"

[email protected]:
version "0.8.0"
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==

"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
Expand Down Expand Up @@ -5863,6 +5898,11 @@ micromatch@^4.0.2:
braces "^3.0.1"
picomatch "^2.0.5"

[email protected]:
version "0.2.0"
resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39"
integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==

[email protected]:
version "1.42.0"
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz#3e252907b4c7adb906597b4b65636272cf9e7bac"
Expand Down Expand Up @@ -5945,6 +5985,13 @@ nan@^2.12.1:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==

[email protected]:
version "1.0.0"
resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef"
integrity sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8=
dependencies:
big-integer "^1.6.16"

nanoid@^3.0.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.3.tgz#b2bcfcfda4b4d6838bc22a0c8dd3c0a17a204c20"
Expand Down Expand Up @@ -6823,7 +6870,7 @@ [email protected]:
dependencies:
glob "^7.1.3"

rimraf@^3.0.0, rimraf@^3.0.2:
rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
Expand Down Expand Up @@ -7755,6 +7802,14 @@ universalify@^0.1.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==

[email protected]:
version "2.2.0"
resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7"
integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==
dependencies:
"@babel/runtime" "^7.6.2"
detect-node "^2.0.4"

unquote@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544"
Expand Down

0 comments on commit 4bbfe2a

Please sign in to comment.