mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-11-27 14:55:20 +03:00
Implement support for query client configuration (#612)
* Implement support for query client configuration * Make sure query client is always initialized * Fix formatting * Fix formatting * Add missing await in index.js * Separate query client setup into two functions * Introduce default query client config * Fix missing export in docs * Fix missing field warnings * Add query client setup docs * Update e2e tests for query client config
This commit is contained in:
parent
b19e95ea51
commit
0b78f658be
@ -2,9 +2,9 @@
|
||||
import { callOperation } from '../operations'
|
||||
import * as resources from '../operations/resources'
|
||||
|
||||
const {= actionFnName =} = async (args) => {
|
||||
async function {= actionFnName =}(args) {
|
||||
const actionResult = await callOperation('{= actionRoute =}', args)
|
||||
resources.invalidateQueriesUsing(entitiesUsed)
|
||||
await resources.invalidateQueriesUsing(entitiesUsed)
|
||||
return actionResult
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ export default async function login(email, password) {
|
||||
// TODO(filip): We are currently removing all the queries, but we should
|
||||
// remove only non-public, user-dependent queries - public queries are
|
||||
// expected not to change in respect to the currently logged in user.
|
||||
removeQueries()
|
||||
await removeQueries()
|
||||
} catch (error) {
|
||||
handleApiError(error)
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { clearLocalStorage } from '../api.js'
|
||||
import { invalidateAndRemoveQueries } from '../operations/resources'
|
||||
|
||||
export default function logout() {
|
||||
export default async function logout() {
|
||||
clearLocalStorage()
|
||||
// TODO(filip): We are currently invalidating and removing all the queries, but
|
||||
// we should remove only the non-public, user-dependent ones.
|
||||
invalidateAndRemoveQueries()
|
||||
await invalidateAndRemoveQueries()
|
||||
}
|
||||
|
@ -4,7 +4,10 @@ import ReactDOM from 'react-dom'
|
||||
import { QueryClientProvider } from 'react-query'
|
||||
|
||||
import router from './router'
|
||||
import { queryClient } from './queryClient'
|
||||
import {
|
||||
initializeQueryClient,
|
||||
queryClientInitialized,
|
||||
} from './queryClient'
|
||||
import * as serviceWorker from './serviceWorker'
|
||||
|
||||
{=# doesClientSetupFnExist =}
|
||||
@ -19,8 +22,9 @@ async function startApp() {
|
||||
{=# doesClientSetupFnExist =}
|
||||
await {= clientSetupJsFnIdentifier =}()
|
||||
{=/ doesClientSetupFnExist =}
|
||||
initializeQueryClient()
|
||||
|
||||
render()
|
||||
await render()
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
@ -28,7 +32,8 @@ async function startApp() {
|
||||
serviceWorker.unregister()
|
||||
}
|
||||
|
||||
function render() {
|
||||
async function render() {
|
||||
const queryClient = await queryClientInitialized
|
||||
ReactDOM.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ router }
|
||||
|
@ -1,5 +1,5 @@
|
||||
{{= {= =} =}}
|
||||
import { queryClient } from '../queryClient'
|
||||
import { queryClientInitialized } from '../queryClient'
|
||||
|
||||
|
||||
// Map where key is resource name and value is Set
|
||||
@ -31,26 +31,26 @@ export function addResourcesUsedByQuery(queryCacheKey, resources) {
|
||||
export function getQueriesUsingResource(resource) {
|
||||
return Array.from(resourceToQueryCacheKeys.get(resource) || [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates all queries that are using specified resources.
|
||||
* @param {string[]} resources - Names of resources.
|
||||
*/
|
||||
export function invalidateQueriesUsing(resources) {
|
||||
const queryCacheKeysToInvalidate = new Set()
|
||||
for (const resource of resources) {
|
||||
getQueriesUsingResource(resource).forEach(key => queryCacheKeysToInvalidate.add(key))
|
||||
}
|
||||
for (const queryCacheKey of queryCacheKeysToInvalidate) {
|
||||
export async function invalidateQueriesUsing(resources) {
|
||||
const queryClient = await queryClientInitialized
|
||||
|
||||
const queryCacheKeysToInvalidate = new Set(resources.flatMap(getQueriesUsingResource))
|
||||
queryCacheKeysToInvalidate.forEach(queryCacheKey =>
|
||||
queryClient.invalidateQueries(queryCacheKey)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function removeQueries() {
|
||||
export async function removeQueries() {
|
||||
const queryClient = await queryClientInitialized
|
||||
queryClient.removeQueries()
|
||||
}
|
||||
|
||||
export function invalidateAndRemoveQueries() {
|
||||
export async function invalidateAndRemoveQueries() {
|
||||
const queryClient = await queryClientInitialized
|
||||
// If we don't invalidate the queries before removing them, Wasp will stay on
|
||||
// the same page. The user would have to manually refresh the page to "finish"
|
||||
// logging out.
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useQuery as rqUseQuery } from 'react-query'
|
||||
export { configureQueryClient } from '../queryClient'
|
||||
|
||||
export function useQuery(queryFn, queryFnArgs, config) {
|
||||
export function useQuery(queryFn, queryFnArgs, options) {
|
||||
if (typeof queryFn !== 'function') {
|
||||
throw new TypeError('useQuery requires queryFn to be a function.')
|
||||
}
|
||||
@ -11,6 +12,6 @@ export function useQuery(queryFn, queryFnArgs, config) {
|
||||
return rqUseQuery({
|
||||
queryKey: [queryFn.queryCacheKey, queryFnArgs],
|
||||
queryFn: () => queryFn(queryFnArgs),
|
||||
...config
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
@ -1,3 +1,23 @@
|
||||
import { QueryClient } from 'react-query'
|
||||
|
||||
export const queryClient = new QueryClient()
|
||||
const defaultQueryClientConfig = {}
|
||||
|
||||
let queryClientConfig, resolveQueryClientInitialized, isQueryClientInitialized
|
||||
|
||||
export const queryClientInitialized = new Promise(resolve => {
|
||||
resolveQueryClientInitialized = resolve
|
||||
});
|
||||
|
||||
export function configureQueryClient(config) {
|
||||
if (isQueryClientInitialized) {
|
||||
throw new Error("Attempted to configure the QueryClient after initialization")
|
||||
}
|
||||
|
||||
queryClientConfig = config
|
||||
}
|
||||
|
||||
export function initializeQueryClient() {
|
||||
const queryClient = new QueryClient(queryClientConfig ?? defaultQueryClientConfig)
|
||||
isQueryClientInitialized = true;
|
||||
resolveQueryClientInitialized(queryClient)
|
||||
}
|
||||
|
@ -291,7 +291,7 @@
|
||||
"file",
|
||||
"web-app/src/index.js"
|
||||
],
|
||||
"a477184a6e6e7cb33e056febc8a4a1e1132ecc697763855fa2af07cf02c79908"
|
||||
"d0aeef2633b26de4c5d7064acc7c4533212cee8d3b36e91a3a785930621d02e4"
|
||||
],
|
||||
[
|
||||
[
|
||||
@ -312,21 +312,21 @@
|
||||
"file",
|
||||
"web-app/src/operations/resources.js"
|
||||
],
|
||||
"4772a216b647ace3eedace9b02897922725b58131031d21be59187b1652ccade"
|
||||
"9bccf509e1a3e04e24444eacd69e0f093e590a9b85325bedb6f1d52ac85ead3c"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"web-app/src/queries/index.js"
|
||||
],
|
||||
"39a659c6ed82cbbc38b834b607e049646e0d70e104d01317830f225629a5f14c"
|
||||
"8510924f80b32cc0f67ca39fad9d97c4ce5d9695015da3ec8457e91a88868c78"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"web-app/src/queryClient.js"
|
||||
],
|
||||
"f53c6e82b0e1b13dd2c2fff423460c88cd1c331e154867edeb7cd5f98c3586e4"
|
||||
"3adf1c16b436ce5eb71f0ef7a4ec2bcd04e0dafbc2b43291c5050d1621cb764c"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -3,7 +3,10 @@ import ReactDOM from 'react-dom'
|
||||
import { QueryClientProvider } from 'react-query'
|
||||
|
||||
import router from './router'
|
||||
import { queryClient } from './queryClient'
|
||||
import {
|
||||
initializeQueryClient,
|
||||
queryClientInitialized,
|
||||
} from './queryClient'
|
||||
import * as serviceWorker from './serviceWorker'
|
||||
|
||||
|
||||
@ -12,8 +15,9 @@ import './index.css'
|
||||
startApp()
|
||||
|
||||
async function startApp() {
|
||||
initializeQueryClient()
|
||||
|
||||
render()
|
||||
await render()
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
@ -21,7 +25,8 @@ async function startApp() {
|
||||
serviceWorker.unregister()
|
||||
}
|
||||
|
||||
function render() {
|
||||
async function render() {
|
||||
const queryClient = await queryClientInitialized
|
||||
ReactDOM.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ router }
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { queryClient } from '../queryClient'
|
||||
import { queryClientInitialized } from '../queryClient'
|
||||
|
||||
|
||||
// Map where key is resource name and value is Set
|
||||
@ -30,26 +30,26 @@ export function addResourcesUsedByQuery(queryCacheKey, resources) {
|
||||
export function getQueriesUsingResource(resource) {
|
||||
return Array.from(resourceToQueryCacheKeys.get(resource) || [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates all queries that are using specified resources.
|
||||
* @param {string[]} resources - Names of resources.
|
||||
*/
|
||||
export function invalidateQueriesUsing(resources) {
|
||||
const queryCacheKeysToInvalidate = new Set()
|
||||
for (const resource of resources) {
|
||||
getQueriesUsingResource(resource).forEach(key => queryCacheKeysToInvalidate.add(key))
|
||||
}
|
||||
for (const queryCacheKey of queryCacheKeysToInvalidate) {
|
||||
export async function invalidateQueriesUsing(resources) {
|
||||
const queryClient = await queryClientInitialized
|
||||
|
||||
const queryCacheKeysToInvalidate = new Set(resources.flatMap(getQueriesUsingResource))
|
||||
queryCacheKeysToInvalidate.forEach(queryCacheKey =>
|
||||
queryClient.invalidateQueries(queryCacheKey)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function removeQueries() {
|
||||
export async function removeQueries() {
|
||||
const queryClient = await queryClientInitialized
|
||||
queryClient.removeQueries()
|
||||
}
|
||||
|
||||
export function invalidateAndRemoveQueries() {
|
||||
export async function invalidateAndRemoveQueries() {
|
||||
const queryClient = await queryClientInitialized
|
||||
// If we don't invalidate the queries before removing them, Wasp will stay on
|
||||
// the same page. The user would have to manually refresh the page to "finish"
|
||||
// logging out.
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useQuery as rqUseQuery } from 'react-query'
|
||||
export { configureQueryClient } from '../queryClient'
|
||||
|
||||
export function useQuery(queryFn, queryFnArgs, config) {
|
||||
export function useQuery(queryFn, queryFnArgs, options) {
|
||||
if (typeof queryFn !== 'function') {
|
||||
throw new TypeError('useQuery requires queryFn to be a function.')
|
||||
}
|
||||
@ -11,6 +12,6 @@ export function useQuery(queryFn, queryFnArgs, config) {
|
||||
return rqUseQuery({
|
||||
queryKey: [queryFn.queryCacheKey, queryFnArgs],
|
||||
queryFn: () => queryFn(queryFnArgs),
|
||||
...config
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
@ -1,3 +1,23 @@
|
||||
import { QueryClient } from 'react-query'
|
||||
|
||||
export const queryClient = new QueryClient()
|
||||
const defaultQueryClientConfig = {}
|
||||
|
||||
let queryClientConfig, resolveQueryClientInitialized, isQueryClientInitialized
|
||||
|
||||
export const queryClientInitialized = new Promise(resolve => {
|
||||
resolveQueryClientInitialized = resolve
|
||||
});
|
||||
|
||||
export function configureQueryClient(config) {
|
||||
if (isQueryClientInitialized) {
|
||||
throw new Error("Attempted to configure the QueryClient after initialization")
|
||||
}
|
||||
|
||||
queryClientConfig = config
|
||||
}
|
||||
|
||||
export function initializeQueryClient() {
|
||||
const queryClient = new QueryClient(queryClientConfig ?? defaultQueryClientConfig)
|
||||
isQueryClientInitialized = true;
|
||||
resolveQueryClientInitialized(queryClient)
|
||||
}
|
||||
|
@ -291,7 +291,7 @@
|
||||
"file",
|
||||
"web-app/src/index.js"
|
||||
],
|
||||
"a477184a6e6e7cb33e056febc8a4a1e1132ecc697763855fa2af07cf02c79908"
|
||||
"d0aeef2633b26de4c5d7064acc7c4533212cee8d3b36e91a3a785930621d02e4"
|
||||
],
|
||||
[
|
||||
[
|
||||
@ -312,21 +312,21 @@
|
||||
"file",
|
||||
"web-app/src/operations/resources.js"
|
||||
],
|
||||
"4772a216b647ace3eedace9b02897922725b58131031d21be59187b1652ccade"
|
||||
"9bccf509e1a3e04e24444eacd69e0f093e590a9b85325bedb6f1d52ac85ead3c"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"web-app/src/queries/index.js"
|
||||
],
|
||||
"39a659c6ed82cbbc38b834b607e049646e0d70e104d01317830f225629a5f14c"
|
||||
"8510924f80b32cc0f67ca39fad9d97c4ce5d9695015da3ec8457e91a88868c78"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"web-app/src/queryClient.js"
|
||||
],
|
||||
"f53c6e82b0e1b13dd2c2fff423460c88cd1c331e154867edeb7cd5f98c3586e4"
|
||||
"3adf1c16b436ce5eb71f0ef7a4ec2bcd04e0dafbc2b43291c5050d1621cb764c"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -3,7 +3,10 @@ import ReactDOM from 'react-dom'
|
||||
import { QueryClientProvider } from 'react-query'
|
||||
|
||||
import router from './router'
|
||||
import { queryClient } from './queryClient'
|
||||
import {
|
||||
initializeQueryClient,
|
||||
queryClientInitialized,
|
||||
} from './queryClient'
|
||||
import * as serviceWorker from './serviceWorker'
|
||||
|
||||
|
||||
@ -12,8 +15,9 @@ import './index.css'
|
||||
startApp()
|
||||
|
||||
async function startApp() {
|
||||
initializeQueryClient()
|
||||
|
||||
render()
|
||||
await render()
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
@ -21,7 +25,8 @@ async function startApp() {
|
||||
serviceWorker.unregister()
|
||||
}
|
||||
|
||||
function render() {
|
||||
async function render() {
|
||||
const queryClient = await queryClientInitialized
|
||||
ReactDOM.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ router }
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { queryClient } from '../queryClient'
|
||||
import { queryClientInitialized } from '../queryClient'
|
||||
|
||||
|
||||
// Map where key is resource name and value is Set
|
||||
@ -30,26 +30,26 @@ export function addResourcesUsedByQuery(queryCacheKey, resources) {
|
||||
export function getQueriesUsingResource(resource) {
|
||||
return Array.from(resourceToQueryCacheKeys.get(resource) || [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates all queries that are using specified resources.
|
||||
* @param {string[]} resources - Names of resources.
|
||||
*/
|
||||
export function invalidateQueriesUsing(resources) {
|
||||
const queryCacheKeysToInvalidate = new Set()
|
||||
for (const resource of resources) {
|
||||
getQueriesUsingResource(resource).forEach(key => queryCacheKeysToInvalidate.add(key))
|
||||
}
|
||||
for (const queryCacheKey of queryCacheKeysToInvalidate) {
|
||||
export async function invalidateQueriesUsing(resources) {
|
||||
const queryClient = await queryClientInitialized
|
||||
|
||||
const queryCacheKeysToInvalidate = new Set(resources.flatMap(getQueriesUsingResource))
|
||||
queryCacheKeysToInvalidate.forEach(queryCacheKey =>
|
||||
queryClient.invalidateQueries(queryCacheKey)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function removeQueries() {
|
||||
export async function removeQueries() {
|
||||
const queryClient = await queryClientInitialized
|
||||
queryClient.removeQueries()
|
||||
}
|
||||
|
||||
export function invalidateAndRemoveQueries() {
|
||||
export async function invalidateAndRemoveQueries() {
|
||||
const queryClient = await queryClientInitialized
|
||||
// If we don't invalidate the queries before removing them, Wasp will stay on
|
||||
// the same page. The user would have to manually refresh the page to "finish"
|
||||
// logging out.
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useQuery as rqUseQuery } from 'react-query'
|
||||
export { configureQueryClient } from '../queryClient'
|
||||
|
||||
export function useQuery(queryFn, queryFnArgs, config) {
|
||||
export function useQuery(queryFn, queryFnArgs, options) {
|
||||
if (typeof queryFn !== 'function') {
|
||||
throw new TypeError('useQuery requires queryFn to be a function.')
|
||||
}
|
||||
@ -11,6 +12,6 @@ export function useQuery(queryFn, queryFnArgs, config) {
|
||||
return rqUseQuery({
|
||||
queryKey: [queryFn.queryCacheKey, queryFnArgs],
|
||||
queryFn: () => queryFn(queryFnArgs),
|
||||
...config
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
@ -1,3 +1,23 @@
|
||||
import { QueryClient } from 'react-query'
|
||||
|
||||
export const queryClient = new QueryClient()
|
||||
const defaultQueryClientConfig = {}
|
||||
|
||||
let queryClientConfig, resolveQueryClientInitialized, isQueryClientInitialized
|
||||
|
||||
export const queryClientInitialized = new Promise(resolve => {
|
||||
resolveQueryClientInitialized = resolve
|
||||
});
|
||||
|
||||
export function configureQueryClient(config) {
|
||||
if (isQueryClientInitialized) {
|
||||
throw new Error("Attempted to configure the QueryClient after initialization")
|
||||
}
|
||||
|
||||
queryClientConfig = config
|
||||
}
|
||||
|
||||
export function initializeQueryClient() {
|
||||
const queryClient = new QueryClient(queryClientConfig ?? defaultQueryClientConfig)
|
||||
isQueryClientInitialized = true;
|
||||
resolveQueryClientInitialized(queryClient)
|
||||
}
|
||||
|
@ -312,7 +312,7 @@
|
||||
"file",
|
||||
"web-app/src/index.js"
|
||||
],
|
||||
"a477184a6e6e7cb33e056febc8a4a1e1132ecc697763855fa2af07cf02c79908"
|
||||
"d0aeef2633b26de4c5d7064acc7c4533212cee8d3b36e91a3a785930621d02e4"
|
||||
],
|
||||
[
|
||||
[
|
||||
@ -333,21 +333,21 @@
|
||||
"file",
|
||||
"web-app/src/operations/resources.js"
|
||||
],
|
||||
"4772a216b647ace3eedace9b02897922725b58131031d21be59187b1652ccade"
|
||||
"9bccf509e1a3e04e24444eacd69e0f093e590a9b85325bedb6f1d52ac85ead3c"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"web-app/src/queries/index.js"
|
||||
],
|
||||
"39a659c6ed82cbbc38b834b607e049646e0d70e104d01317830f225629a5f14c"
|
||||
"8510924f80b32cc0f67ca39fad9d97c4ce5d9695015da3ec8457e91a88868c78"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"web-app/src/queryClient.js"
|
||||
],
|
||||
"f53c6e82b0e1b13dd2c2fff423460c88cd1c331e154867edeb7cd5f98c3586e4"
|
||||
"3adf1c16b436ce5eb71f0ef7a4ec2bcd04e0dafbc2b43291c5050d1621cb764c"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -3,7 +3,10 @@ import ReactDOM from 'react-dom'
|
||||
import { QueryClientProvider } from 'react-query'
|
||||
|
||||
import router from './router'
|
||||
import { queryClient } from './queryClient'
|
||||
import {
|
||||
initializeQueryClient,
|
||||
queryClientInitialized,
|
||||
} from './queryClient'
|
||||
import * as serviceWorker from './serviceWorker'
|
||||
|
||||
|
||||
@ -12,8 +15,9 @@ import './index.css'
|
||||
startApp()
|
||||
|
||||
async function startApp() {
|
||||
initializeQueryClient()
|
||||
|
||||
render()
|
||||
await render()
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
@ -21,7 +25,8 @@ async function startApp() {
|
||||
serviceWorker.unregister()
|
||||
}
|
||||
|
||||
function render() {
|
||||
async function render() {
|
||||
const queryClient = await queryClientInitialized
|
||||
ReactDOM.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ router }
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { queryClient } from '../queryClient'
|
||||
import { queryClientInitialized } from '../queryClient'
|
||||
|
||||
|
||||
// Map where key is resource name and value is Set
|
||||
@ -30,26 +30,26 @@ export function addResourcesUsedByQuery(queryCacheKey, resources) {
|
||||
export function getQueriesUsingResource(resource) {
|
||||
return Array.from(resourceToQueryCacheKeys.get(resource) || [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates all queries that are using specified resources.
|
||||
* @param {string[]} resources - Names of resources.
|
||||
*/
|
||||
export function invalidateQueriesUsing(resources) {
|
||||
const queryCacheKeysToInvalidate = new Set()
|
||||
for (const resource of resources) {
|
||||
getQueriesUsingResource(resource).forEach(key => queryCacheKeysToInvalidate.add(key))
|
||||
}
|
||||
for (const queryCacheKey of queryCacheKeysToInvalidate) {
|
||||
export async function invalidateQueriesUsing(resources) {
|
||||
const queryClient = await queryClientInitialized
|
||||
|
||||
const queryCacheKeysToInvalidate = new Set(resources.flatMap(getQueriesUsingResource))
|
||||
queryCacheKeysToInvalidate.forEach(queryCacheKey =>
|
||||
queryClient.invalidateQueries(queryCacheKey)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function removeQueries() {
|
||||
export async function removeQueries() {
|
||||
const queryClient = await queryClientInitialized
|
||||
queryClient.removeQueries()
|
||||
}
|
||||
|
||||
export function invalidateAndRemoveQueries() {
|
||||
export async function invalidateAndRemoveQueries() {
|
||||
const queryClient = await queryClientInitialized
|
||||
// If we don't invalidate the queries before removing them, Wasp will stay on
|
||||
// the same page. The user would have to manually refresh the page to "finish"
|
||||
// logging out.
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useQuery as rqUseQuery } from 'react-query'
|
||||
export { configureQueryClient } from '../queryClient'
|
||||
|
||||
export function useQuery(queryFn, queryFnArgs, config) {
|
||||
export function useQuery(queryFn, queryFnArgs, options) {
|
||||
if (typeof queryFn !== 'function') {
|
||||
throw new TypeError('useQuery requires queryFn to be a function.')
|
||||
}
|
||||
@ -11,6 +12,6 @@ export function useQuery(queryFn, queryFnArgs, config) {
|
||||
return rqUseQuery({
|
||||
queryKey: [queryFn.queryCacheKey, queryFnArgs],
|
||||
queryFn: () => queryFn(queryFnArgs),
|
||||
...config
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
@ -1,3 +1,23 @@
|
||||
import { QueryClient } from 'react-query'
|
||||
|
||||
export const queryClient = new QueryClient()
|
||||
const defaultQueryClientConfig = {}
|
||||
|
||||
let queryClientConfig, resolveQueryClientInitialized, isQueryClientInitialized
|
||||
|
||||
export const queryClientInitialized = new Promise(resolve => {
|
||||
resolveQueryClientInitialized = resolve
|
||||
});
|
||||
|
||||
export function configureQueryClient(config) {
|
||||
if (isQueryClientInitialized) {
|
||||
throw new Error("Attempted to configure the QueryClient after initialization")
|
||||
}
|
||||
|
||||
queryClientConfig = config
|
||||
}
|
||||
|
||||
export function initializeQueryClient() {
|
||||
const queryClient = new QueryClient(queryClientConfig ?? defaultQueryClientConfig)
|
||||
isQueryClientInitialized = true;
|
||||
resolveQueryClientInitialized(queryClient)
|
||||
}
|
||||
|
@ -291,7 +291,7 @@
|
||||
"file",
|
||||
"web-app/src/index.js"
|
||||
],
|
||||
"a477184a6e6e7cb33e056febc8a4a1e1132ecc697763855fa2af07cf02c79908"
|
||||
"d0aeef2633b26de4c5d7064acc7c4533212cee8d3b36e91a3a785930621d02e4"
|
||||
],
|
||||
[
|
||||
[
|
||||
@ -312,21 +312,21 @@
|
||||
"file",
|
||||
"web-app/src/operations/resources.js"
|
||||
],
|
||||
"4772a216b647ace3eedace9b02897922725b58131031d21be59187b1652ccade"
|
||||
"9bccf509e1a3e04e24444eacd69e0f093e590a9b85325bedb6f1d52ac85ead3c"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"web-app/src/queries/index.js"
|
||||
],
|
||||
"39a659c6ed82cbbc38b834b607e049646e0d70e104d01317830f225629a5f14c"
|
||||
"8510924f80b32cc0f67ca39fad9d97c4ce5d9695015da3ec8457e91a88868c78"
|
||||
],
|
||||
[
|
||||
[
|
||||
"file",
|
||||
"web-app/src/queryClient.js"
|
||||
],
|
||||
"f53c6e82b0e1b13dd2c2fff423460c88cd1c331e154867edeb7cd5f98c3586e4"
|
||||
"3adf1c16b436ce5eb71f0ef7a4ec2bcd04e0dafbc2b43291c5050d1621cb764c"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -3,7 +3,10 @@ import ReactDOM from 'react-dom'
|
||||
import { QueryClientProvider } from 'react-query'
|
||||
|
||||
import router from './router'
|
||||
import { queryClient } from './queryClient'
|
||||
import {
|
||||
initializeQueryClient,
|
||||
queryClientInitialized,
|
||||
} from './queryClient'
|
||||
import * as serviceWorker from './serviceWorker'
|
||||
|
||||
|
||||
@ -12,8 +15,9 @@ import './index.css'
|
||||
startApp()
|
||||
|
||||
async function startApp() {
|
||||
initializeQueryClient()
|
||||
|
||||
render()
|
||||
await render()
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
@ -21,7 +25,8 @@ async function startApp() {
|
||||
serviceWorker.unregister()
|
||||
}
|
||||
|
||||
function render() {
|
||||
async function render() {
|
||||
const queryClient = await queryClientInitialized
|
||||
ReactDOM.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ router }
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { queryClient } from '../queryClient'
|
||||
import { queryClientInitialized } from '../queryClient'
|
||||
|
||||
|
||||
// Map where key is resource name and value is Set
|
||||
@ -30,26 +30,26 @@ export function addResourcesUsedByQuery(queryCacheKey, resources) {
|
||||
export function getQueriesUsingResource(resource) {
|
||||
return Array.from(resourceToQueryCacheKeys.get(resource) || [])
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates all queries that are using specified resources.
|
||||
* @param {string[]} resources - Names of resources.
|
||||
*/
|
||||
export function invalidateQueriesUsing(resources) {
|
||||
const queryCacheKeysToInvalidate = new Set()
|
||||
for (const resource of resources) {
|
||||
getQueriesUsingResource(resource).forEach(key => queryCacheKeysToInvalidate.add(key))
|
||||
}
|
||||
for (const queryCacheKey of queryCacheKeysToInvalidate) {
|
||||
export async function invalidateQueriesUsing(resources) {
|
||||
const queryClient = await queryClientInitialized
|
||||
|
||||
const queryCacheKeysToInvalidate = new Set(resources.flatMap(getQueriesUsingResource))
|
||||
queryCacheKeysToInvalidate.forEach(queryCacheKey =>
|
||||
queryClient.invalidateQueries(queryCacheKey)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export function removeQueries() {
|
||||
export async function removeQueries() {
|
||||
const queryClient = await queryClientInitialized
|
||||
queryClient.removeQueries()
|
||||
}
|
||||
|
||||
export function invalidateAndRemoveQueries() {
|
||||
export async function invalidateAndRemoveQueries() {
|
||||
const queryClient = await queryClientInitialized
|
||||
// If we don't invalidate the queries before removing them, Wasp will stay on
|
||||
// the same page. The user would have to manually refresh the page to "finish"
|
||||
// logging out.
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { useQuery as rqUseQuery } from 'react-query'
|
||||
export { configureQueryClient } from '../queryClient'
|
||||
|
||||
export function useQuery(queryFn, queryFnArgs, config) {
|
||||
export function useQuery(queryFn, queryFnArgs, options) {
|
||||
if (typeof queryFn !== 'function') {
|
||||
throw new TypeError('useQuery requires queryFn to be a function.')
|
||||
}
|
||||
@ -11,6 +12,6 @@ export function useQuery(queryFn, queryFnArgs, config) {
|
||||
return rqUseQuery({
|
||||
queryKey: [queryFn.queryCacheKey, queryFnArgs],
|
||||
queryFn: () => queryFn(queryFnArgs),
|
||||
...config
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
@ -1,3 +1,23 @@
|
||||
import { QueryClient } from 'react-query'
|
||||
|
||||
export const queryClient = new QueryClient()
|
||||
const defaultQueryClientConfig = {}
|
||||
|
||||
let queryClientConfig, resolveQueryClientInitialized, isQueryClientInitialized
|
||||
|
||||
export const queryClientInitialized = new Promise(resolve => {
|
||||
resolveQueryClientInitialized = resolve
|
||||
});
|
||||
|
||||
export function configureQueryClient(config) {
|
||||
if (isQueryClientInitialized) {
|
||||
throw new Error("Attempted to configure the QueryClient after initialization")
|
||||
}
|
||||
|
||||
queryClientConfig = config
|
||||
}
|
||||
|
||||
export function initializeQueryClient() {
|
||||
const queryClient = new QueryClient(queryClientConfig ?? defaultQueryClientConfig)
|
||||
isQueryClientInitialized = true;
|
||||
resolveQueryClientInitialized(queryClient)
|
||||
}
|
||||
|
@ -132,6 +132,7 @@ spec_AppSpecValid = do
|
||||
{ AS.App.title = "Test App",
|
||||
AS.App.db = Nothing,
|
||||
AS.App.server = Nothing,
|
||||
AS.App.client = Nothing,
|
||||
AS.App.auth = Nothing,
|
||||
AS.App.dependencies = Nothing,
|
||||
AS.App.head = Nothing
|
||||
|
@ -30,6 +30,7 @@ spec_WebAppGenerator = do
|
||||
{ AS.App.title = "Test App",
|
||||
AS.App.db = Nothing,
|
||||
AS.App.server = Nothing,
|
||||
AS.App.client = Nothing,
|
||||
AS.App.auth = Nothing,
|
||||
AS.App.dependencies = Nothing,
|
||||
AS.App.head = Nothing
|
||||
|
@ -284,8 +284,12 @@ This hook comes bundled with Wasp and is a thin wrapper around the `useQuery` ho
|
||||
|
||||
Wasp's `useQuery` hook accepts three arguments:
|
||||
- `queryFn` (required): A Wasp query declared in the previous step or, in other words, the client-side query function generated by Wasp based on a `query` declaration.
|
||||
- `queryFnArgs` (optional): The arguments object (payload) you wish to pass into the query. The query's NodeJS implementation will receive this object as its first positional argument.
|
||||
- `config` (optional): A _react-query_ `config` object.
|
||||
- `queryFnArgs` (optional): The arguments object (payload) you wish to pass into the Query. The Query's NodeJS implementation will receive this object as its first positional argument.
|
||||
- `options` (optional): A _react-query_ `options` object. Use this to change
|
||||
[the default
|
||||
behaviour](https://react-query.tanstack.com/guides/important-defaults) for
|
||||
this particular query. If you want to change the global defaults, you can do
|
||||
so in the [client setup function](#overriding-default-behaviour-for-queries).
|
||||
|
||||
Wasp's `useQuery` hook behaves mostly the same as [_react-query_'s `useQuery` hook](https://react-query.tanstack.com/docs/api#usequery), the only difference being in not having to supply the key (Wasp does this automatically under the hood).
|
||||
|
||||
@ -961,7 +965,7 @@ client-side periodic jobs).
|
||||
Here's a dummy example of such a function:
|
||||
|
||||
```js title="ext/myClientSetupCode.js"
|
||||
async function mySetupFunction() {
|
||||
export default async function mySetupFunction() {
|
||||
let count = 1;
|
||||
setInterval(
|
||||
() => console.log(`You have been online for ${count++} hours.`),
|
||||
@ -970,6 +974,38 @@ async function mySetupFunction() {
|
||||
}
|
||||
```
|
||||
|
||||
##### Overriding default behaviour for Queries
|
||||
As mentioned, our `useQuery` hook uses _react-query_'s hook of the same name.
|
||||
Since _react-query_ comes configured with aggressive but sane default options,
|
||||
you most likely won't have to change those defaults for all Queries (you can
|
||||
change them for a single Query using the `options` object, as described
|
||||
[here](#the-usequery-hook)).
|
||||
|
||||
Still, if you do need the global defaults, you can do so inside client setup
|
||||
function. Wasp exposes a `configureQueryClient` hook that lets you configure
|
||||
_react-query_'s `QueryClient` object:
|
||||
|
||||
|
||||
```js title="ext/myClientSetupCode.js"
|
||||
import { configureQueryClient } from '@wasp/queries'
|
||||
|
||||
export default async function mySetupFunction() {
|
||||
// ... some setup
|
||||
configureQueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: Infinity,
|
||||
}
|
||||
}
|
||||
})
|
||||
// ... some more setup
|
||||
}
|
||||
```
|
||||
|
||||
Make sure to pass in an object expected by the `QueryClient`'s construcor, as
|
||||
explained in
|
||||
[_react-query_'s docs](https://react-query.tanstack.com/reference/QueryClient).
|
||||
|
||||
## Server configuration
|
||||
|
||||
Via `server` field of `app` declaration, you can configure behaviour of the Node.js server (one that is executing wasp operations).
|
||||
|
Loading…
Reference in New Issue
Block a user