Repository navigation
feat(core): support the same query with different variables in useFaustQuery #2695
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
452fe89
feat(core): read results of the same query run with different variabl…
josephfusco 18d7aaa
docs: show how to run the same query with different variables
josephfusco 99fc5b1
docs: note that repeated queries' variables shouldn't depend on extra
josephfusco 1aabc35
feat(core): export the UseFaustQueryOptions type
josephfusco dc64b31
test(core): cover repeated queries in getWordPressProps
josephfusco debaaa8
fix(core): key query variables the way Apollo sends them and keep onl…
josephfusco fa25ff0
test(core): resolve client.query mocks with promises like Apollo does
josephfusco File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,26 @@ | ||
| import { DocumentNode } from '@apollo/client'; | ||
| // eslint-disable-next-line import/extensions | ||
| import { print } from '@apollo/client/utilities'; | ||
| import { sha256 } from 'js-sha256'; | ||
| import { useContext } from 'react'; | ||
| import { FaustContext } from '../store/FaustContext.js'; | ||
| import { readFaustQuery } from '../store/faustQueries.js'; | ||
|
|
||
| export function useFaustQuery<TData>(query: DocumentNode): TData { | ||
| export type UseFaustQueryOptions = { | ||
| /** | ||
| * The variables the query was run with in `Component.queries`. Pass them | ||
| * when a template runs the same query more than once with different | ||
| * variables, to get the result for these variables. | ||
| */ | ||
| variables?: { [key: string]: any }; | ||
| }; | ||
|
|
||
| export function useFaustQuery<TData>( | ||
| query: DocumentNode, | ||
| options?: UseFaustQueryOptions, | ||
| ): TData { | ||
| const context = useContext(FaustContext); | ||
|
|
||
| if (context === undefined) { | ||
| throw new Error('useFaustQuery must be used within a FaustProvider'); | ||
| } | ||
|
|
||
| const sha = sha256(print(query)); | ||
|
|
||
| // eslint-disable-next-line no-underscore-dangle | ||
| return context?.queries?.[sha] as TData; | ||
| return readFaustQuery(context?.queries, query, options?.variables) as TData; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| import { DocumentNode } from '@apollo/client'; | ||
| // eslint-disable-next-line import/extensions | ||
| import { print } from '@apollo/client/utilities'; | ||
| import { sha256 } from 'js-sha256'; | ||
| import type { FaustQueries } from './FaustContext.js'; | ||
|
|
||
| type Variables = { [key: string]: any }; | ||
|
|
||
| // Suffix for the entry that records which variables the query-only key's | ||
| // result was fetched with. | ||
| const VARIABLES_SUFFIX = ':variables'; | ||
|
|
||
| function sortKeys(value: unknown): unknown { | ||
| if (Array.isArray(value)) { | ||
| return value.map(sortKeys); | ||
| } | ||
|
|
||
| if (value !== null && typeof value === 'object') { | ||
| return Object.keys(value) | ||
| .sort() | ||
| .reduce<Variables>((sorted, key) => { | ||
| // eslint-disable-next-line no-param-reassign | ||
| sorted[key] = sortKeys((value as Variables)[key]); | ||
| return sorted; | ||
| }, {}); | ||
| } | ||
|
|
||
| return value; | ||
| } | ||
|
|
||
| /** | ||
| * Get the key a query's result is stored under. With variables, the key also | ||
| * covers them, in any key order, so the same query can be stored once per set | ||
| * of variables. Variables go through JSON first, as they do when Apollo sends | ||
| * them, so values like dates keep what makes them different. | ||
| */ | ||
| export function getFaustQueryKey( | ||
| query: DocumentNode, | ||
| variables?: Variables, | ||
| ): string { | ||
| const printed = print(query); | ||
|
|
||
| return variables === undefined | ||
| ? sha256(printed) | ||
| : sha256( | ||
| `${printed}\n${JSON.stringify( | ||
| sortKeys(JSON.parse(JSON.stringify(variables))), | ||
| )}`, | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Store the results of a template's `queries`. | ||
| * | ||
| * Each result is stored once. The query-only key holds the last result for | ||
| * each query, as it always has, so `useFaustQuery(query)` keeps working. | ||
| * Results with variables that aren't under the query-only key are stored | ||
| * under a key for the query and its variables. | ||
| */ | ||
| export function storeFaustQueries( | ||
| results: { | ||
| query: DocumentNode; | ||
| variables?: Variables; | ||
| data: any; | ||
| }[], | ||
| ): FaustQueries { | ||
| const queries: FaustQueries = {}; | ||
| const entries = results.map(({ query, variables, data }) => ({ | ||
| queryKey: getFaustQueryKey(query), | ||
| variablesKey: | ||
| variables === undefined ? undefined : getFaustQueryKey(query, variables), | ||
| data, | ||
| })); | ||
| const lastIndex = new Map<string, number>(); | ||
|
|
||
| // A later entry for the same query and variables replaces an earlier one. | ||
| entries.forEach(({ queryKey, variablesKey }, index) => { | ||
| lastIndex.set(queryKey, index); | ||
| if (variablesKey) { | ||
| lastIndex.set(variablesKey, index); | ||
| } | ||
| }); | ||
|
|
||
| entries.forEach(({ queryKey, variablesKey, data }, index) => { | ||
| if (lastIndex.get(queryKey) === index) { | ||
| queries[queryKey] = data; | ||
|
|
||
| if (variablesKey) { | ||
| queries[queryKey + VARIABLES_SUFFIX] = variablesKey; | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| entries.forEach(({ queryKey, variablesKey, data }, index) => { | ||
| if ( | ||
| variablesKey && | ||
| lastIndex.get(variablesKey) === index && | ||
| queries[queryKey + VARIABLES_SUFFIX] !== variablesKey | ||
| ) { | ||
| queries[variablesKey] = data; | ||
| } | ||
| }); | ||
|
|
||
| return queries; | ||
| } | ||
|
|
||
| /** | ||
| * Read a stored query result, optionally the one for specific variables. | ||
| */ | ||
| export function readFaustQuery( | ||
| queries: FaustQueries | null | undefined, | ||
| query: DocumentNode, | ||
| variables?: Variables, | ||
| ): any { | ||
| const queryKey = getFaustQueryKey(query); | ||
|
|
||
| if (variables === undefined) { | ||
| return queries?.[queryKey]; | ||
| } | ||
|
|
||
| const variablesKey = getFaustQueryKey(query, variables); | ||
|
|
||
| if (queries?.[variablesKey] !== undefined) { | ||
| return queries[variablesKey]; | ||
| } | ||
|
|
||
| return queries?.[queryKey + VARIABLES_SUFFIX] === variablesKey | ||
| ? queries[queryKey] | ||
| : undefined; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.