Skip to content

Commit 3efd9bf

Browse files
Update query hooks and introduce window state getters (#36257)
* Add useWindowStateQuery * changelog * Move hook to get function * Fix typo * Update query hooks * Remove explicitKey param
1 parent 81430ae commit 3efd9bf

File tree

17 files changed

+154
-108
lines changed

17 files changed

+154
-108
lines changed

projects/packages/my-jetpack/_inc/components/add-license-screen/index.jsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ import GoBackLink from '../go-back-link';
2020
*/
2121
export default function AddLicenseScreen() {
2222
const { recordEvent } = useAnalytics();
23-
const { data: licenses = [], isLoading: fetchingAvailableLicenses } = useJetpackApiQuery(
24-
QUERY_LICENSES_KEY,
25-
async api => ( await api.getUserLicenses() )?.items
26-
);
23+
const { data: licenses = [], isLoading: fetchingAvailableLicenses } = useJetpackApiQuery( {
24+
name: QUERY_LICENSES_KEY,
25+
queryFn: async api => ( await api.getUserLicenses() )?.items,
26+
} );
2727
const { userConnectionData } = useConnection();
2828
const [ hasActivatedLicense, setHasActivatedLicense ] = useState( false );
2929

projects/packages/my-jetpack/_inc/components/my-jetpack-screen/index.jsx

+17-17
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
} from '../../data/constants';
3030
import useProduct from '../../data/products/use-product';
3131
import useSimpleQuery from '../../data/use-simple-query';
32+
import getMyJetpackWindowState from '../../data/utils/get-my-jetpack-window-state';
3233
import useAnalytics from '../../hooks/use-analytics';
3334
import useConnectionWatcher from '../../hooks/use-connection-watcher';
3435
import ConnectionsSection from '../connections-section';
@@ -94,28 +95,27 @@ const GlobalNotice = ( { message, options } ) => {
9495
export default function MyJetpackScreen() {
9596
useConnectionWatcher();
9697
// Check using the global state instead of Redux so it only has effect after refreshing the page
97-
const welcomeBannerHasBeenDismissed =
98-
window?.myJetpackInitialState?.welcomeBanner.hasBeenDismissed;
99-
const { showJetpackStatsCard = false } = window.myJetpackInitialState?.myJetpackFlags ?? {};
100-
const jetpackManage = window?.myJetpackInitialState?.jetpackManage;
98+
const { hasBeenDismissed: welcomeBannerHasBeenDismissed } = getMyJetpackWindowState(
99+
'welcomeBanner',
100+
{ hasBeenDismissed: false }
101+
);
102+
const { showJetpackStatsCard } = getMyJetpackWindowState( 'myJetpackFlags', {
103+
showJetpackStatsCard: false,
104+
} );
105+
const jetpackManage = getMyJetpackWindowState( 'jetpackManage', {} );
101106

102107
const { currentNotice } = useContext( NoticeContext );
103108
const { message, options } = currentNotice || {};
104109
const { hasConnectionError } = useConnectionErrorNotice();
105-
const { data: availabilityData, isLoading: isChatAvailabilityLoading } = useSimpleQuery(
106-
QUERY_CHAT_AVAILABILITY_KEY,
107-
{
108-
path: REST_API_CHAT_AVAILABILITY_ENDPOINT,
109-
}
110-
);
110+
const { data: availabilityData, isLoading: isChatAvailabilityLoading } = useSimpleQuery( {
111+
name: QUERY_CHAT_AVAILABILITY_KEY,
112+
query: { path: REST_API_CHAT_AVAILABILITY_ENDPOINT },
113+
} );
111114
const { detail: statsDetails } = useProduct( 'stats' );
112-
113-
const { data: authData, isLoading: isJwtLoading } = useSimpleQuery(
114-
QUERY_CHAT_AUTHENTICATION_KEY,
115-
{
116-
path: REST_API_CHAT_AUTHENTICATION_ENDPOINT,
117-
}
118-
);
115+
const { data: authData, isLoading: isJwtLoading } = useSimpleQuery( {
116+
name: QUERY_CHAT_AUTHENTICATION_KEY,
117+
query: { path: REST_API_CHAT_AUTHENTICATION_ENDPOINT },
118+
} );
119119

120120
const isAvailable = availabilityData?.is_available;
121121
const jwt = authData?.user?.jwt;

projects/packages/my-jetpack/_inc/components/plans-section/index.jsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,10 @@ export default function PlansSection() {
166166
data: purchases,
167167
isLoading,
168168
isError,
169-
} = useSimpleQuery( QUERY_PURCHASES_KEY, { path: REST_API_SITE_PURCHASES_ENDPOINT } );
169+
} = useSimpleQuery( {
170+
name: QUERY_PURCHASES_KEY,
171+
query: { path: REST_API_SITE_PURCHASES_ENDPOINT },
172+
} );
170173

171174
const isDataLoaded = purchases && ! isLoading && ! isError;
172175
const numberOfPurchases = isDataLoaded ? purchases.length : 0;

projects/packages/my-jetpack/_inc/components/product-cards-section/backup-card/index.jsx

+10-4
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,11 @@ const BackupCard = ( { admin } ) => {
134134
};
135135

136136
const WithBackupsValueSection = ( { admin, slug } ) => {
137-
const { data, isLoading } = useSimpleQuery( QUERY_BACKUP_HISTORY_KEY, {
138-
path: REST_API_REWINDABLE_BACKUP_EVENTS_ENDPOINT,
137+
const { data, isLoading } = useSimpleQuery( {
138+
name: QUERY_BACKUP_HISTORY_KEY,
139+
query: {
140+
path: REST_API_REWINDABLE_BACKUP_EVENTS_ENDPOINT,
141+
},
139142
} );
140143
const lastRewindableEvent = data?.last_rewindable_event;
141144
const lastRewindableEventTime = lastRewindableEvent?.published;
@@ -192,8 +195,11 @@ const WithBackupsValueSection = ( { admin, slug } ) => {
192195

193196
const NoBackupsValueSection = ( { admin, slug } ) => {
194197
const [ itemsToShow, setItemsToShow ] = useState( 3 );
195-
const { data: backupStats, isLoading } = useSimpleQuery( QUERY_BACKUP_STATS_KEY, {
196-
path: REST_API_COUNT_BACKUP_ITEMS_ENDPOINT,
198+
const { data: backupStats, isLoading } = useSimpleQuery( {
199+
name: QUERY_BACKUP_STATS_KEY,
200+
query: {
201+
path: REST_API_COUNT_BACKUP_ITEMS_ENDPOINT,
202+
},
197203
} );
198204

199205
const sortedStats = useMemo( () => {

projects/packages/my-jetpack/_inc/components/product-cards-section/videopress-card.jsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ const useVideoPressStats = () => {
2020
data: stats,
2121
isLoading,
2222
isError,
23-
} = useSimpleQuery( QUERY_VIDEOPRESS_STATS_KEY, { path: REST_API_VIDEOPRESS_FEATURED_STATS } );
23+
} = useSimpleQuery( {
24+
name: QUERY_VIDEOPRESS_STATS_KEY,
25+
query: { path: REST_API_VIDEOPRESS_FEATURED_STATS },
26+
} );
2427

2528
const views = stats?.data?.views ?? {};
2629
const { previous = null, current = null } = views;

projects/packages/my-jetpack/_inc/components/redeem-token-screen/index.jsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ export default function RedeemTokenScreen() {
1818
userConnectionData?.currentUser?.wpcomUser?.display_name ||
1919
userConnectionData?.currentUser?.wpcomUser?.login ||
2020
userConnectionData?.currentUser?.username;
21-
const { isLoading, data: purchases } = useSimpleQuery( QUERY_PURCHASES_KEY, {
22-
path: REST_API_SITE_PURCHASES_ENDPOINT,
21+
const { isLoading, data: purchases } = useSimpleQuery( {
22+
name: QUERY_PURCHASES_KEY,
23+
query: {
24+
path: REST_API_SITE_PURCHASES_ENDPOINT,
25+
},
2326
} );
2427

2528
const tokenRedeemed = includesLifetimePurchase( purchases );

projects/packages/my-jetpack/_inc/components/stats-section/index.jsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@ const StatsSection = () => {
1515
const { detail } = useProduct( slug );
1616
const { status } = detail;
1717
const isAdmin = !! window?.myJetpackInitialState?.userIsAdmin;
18-
const { data: statsCounts } = useSimpleQuery( QUERY_STATS_COUNTS_KEY, {
19-
path: getStatsHighlightsEndpoint( blogID ),
18+
const { data: statsCounts } = useSimpleQuery( {
19+
name: QUERY_STATS_COUNTS_KEY,
20+
query: {
21+
path: getStatsHighlightsEndpoint( blogID ),
22+
},
2023
} );
2124
const counts = statsCounts?.past_seven_days || {};
2225
const previousCounts = statsCounts?.between_past_eight_and_fifteen_days || {};

projects/packages/my-jetpack/_inc/components/welcome-banner/index.jsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useConnection } from '@automattic/jetpack-connection';
33
import { __ } from '@wordpress/i18n';
44
import { close } from '@wordpress/icons';
55
import { useEffect, useCallback, useState } from 'react';
6+
import getMyJetpackWindowState from '../../data/utils/get-my-jetpack-window-state';
67
import useWelcomeBanner from '../../data/welcome-banner/use-welcome-banner';
78
import useAnalytics from '../../hooks/use-analytics';
89
import useMyJetpackNavigate from '../../hooks/use-my-jetpack-navigate';
@@ -15,7 +16,7 @@ import styles from './style.module.scss';
1516
* @returns {object} The WelcomeBanner component.
1617
*/
1718
const WelcomeBanner = () => {
18-
const isNewUser = window.myJetpackInitialState.userIsNewToJetpack === '1';
19+
const isNewUser = getMyJetpackWindowState( 'userIsNewToJetpack', '0' ) === '1';
1920
const { recordEvent } = useAnalytics();
2021
const { isDismissed, dismissWelcomeBanner } = useWelcomeBanner();
2122
const { isRegistered, isUserConnected } = useConnection();

projects/packages/my-jetpack/_inc/data/products/use-activate.ts

+7-8
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,21 @@ import useProduct from './use-product';
77
const useActivate = ( productId: string ) => {
88
const { detail, refetch } = useProduct( productId );
99

10-
const { mutate: activate, isPending } = useSimpleMutation(
11-
QUERY_ACTIVATE_PRODUCT_KEY,
12-
{
10+
const { mutate: activate, isPending } = useSimpleMutation( {
11+
name: QUERY_ACTIVATE_PRODUCT_KEY,
12+
query: {
1313
path: `${ REST_API_SITE_PRODUCTS_ENDPOINT }/${ productId }`,
1414
method: 'POST',
1515
},
16-
{
16+
options: {
1717
onSuccess: refetch,
1818
},
19-
null,
20-
sprintf(
19+
errorMessage: sprintf(
2120
// translators: %$1s: Jetpack Product name
2221
__( 'Failed to activate %1$s. Please try again', 'jetpack-my-jetpack' ),
2322
detail.name
24-
)
25-
);
23+
),
24+
} );
2625

2726
return {
2827
activate,

projects/packages/my-jetpack/_inc/data/products/use-install-standalone-plugin.ts

+7-8
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,21 @@ import useProduct from './use-product';
77
const useInstallStandalonePlugin = ( productId: string ) => {
88
const { detail, refetch } = useProduct( productId );
99

10-
const { mutate: install, isPending } = useSimpleMutation(
11-
QUERY_INSTALL_PRODUCT_KEY,
12-
{
10+
const { mutate: install, isPending } = useSimpleMutation( {
11+
name: QUERY_INSTALL_PRODUCT_KEY,
12+
query: {
1313
path: `${ REST_API_SITE_PRODUCTS_ENDPOINT }/${ productId }/install-standalone`,
1414
method: 'POST',
1515
},
16-
{
16+
options: {
1717
onSuccess: refetch,
1818
},
19-
null,
20-
sprintf(
19+
errorMessage: sprintf(
2120
// translators: %$1s: Jetpack Product name
2221
__( 'Failed to install standalone plugin for %1$s. Please try again', 'jetpack-my-jetpack' ),
2322
detail.name
24-
)
25-
);
23+
),
24+
} );
2625

2726
return {
2827
install,

projects/packages/my-jetpack/_inc/data/products/use-product.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { REST_API_SITE_PRODUCTS_ENDPOINT } from '../constants';
33
import { QUERY_PRODUCT_KEY } from '../constants';
44
import useSimpleQuery from '../use-simple-query';
55
import mapObjectKeysToCamel from '../utils/to-camel';
6-
import type { ProductCamelCase, ProductSnakeCase } from '../types';
6+
import type { ProductCamelCase, ProductSnakeCase, WP_Error } from '../types';
77
import type { RefetchOptions, QueryObserverResult } from '@tanstack/react-query';
88

99
const getFullPricePerMonth = ( product: ProductCamelCase ) => {
@@ -27,21 +27,23 @@ export const useAllProducts = () => {
2727

2828
// Create query to fetch new product data from the server
2929
const useFetchProduct = ( productId: string ) => {
30-
const queryResult = useSimpleQuery< ProductSnakeCase >(
31-
QUERY_PRODUCT_KEY,
32-
{
30+
const queryResult = useSimpleQuery< ProductSnakeCase >( {
31+
name: QUERY_PRODUCT_KEY,
32+
query: {
3333
path: `${ REST_API_SITE_PRODUCTS_ENDPOINT }/${ productId }`,
3434
},
35-
{ enabled: false }
36-
);
35+
options: { enabled: false },
36+
} );
3737

3838
return queryResult;
3939
};
4040

4141
// Fetch the product data from the server and update the global state
4242
const refetchProduct = async (
4343
productId: string,
44-
refetch: ( options?: RefetchOptions ) => Promise< QueryObserverResult< ProductSnakeCase, Error > >
44+
refetch: (
45+
options?: RefetchOptions
46+
) => Promise< QueryObserverResult< ProductSnakeCase, WP_Error > >
4547
) => {
4648
const { data: refetchedProduct } = await refetch();
4749

projects/packages/my-jetpack/_inc/data/use-jetpack-api-query.ts

+17-13
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
11
import restApi from '@automattic/jetpack-api';
22
import { useQuery } from '@tanstack/react-query';
33
import { useFetchingErrorNotice } from './notices/use-fetching-error-notice';
4+
import type { UseQueryResult } from '@tanstack/react-query';
45

56
/**
6-
* A hook to fetch data from the Jetpack API using react-query
7+
* Custom hook for fetching data from the Jetpack API, utilizing the react-query library for data fetching and caching.
8+
* This hook abstracts the common setup needed for calling the Jetpack API, such as setting the API root and nonce,
9+
* and provides react-query's powerful features like caching and automatic refetching.
710
*
8-
* @param {string} name - The name of the query.
9-
* @param {Function} queryFn - The function that fetches the data.
10-
* @param {string} explicitKey - An optional key to use for the query cache.
11-
* @param {string} errorMessage - An optional custom error message to display.
12-
* @returns {Array} The result of the query.
11+
* @template T The type of data expected to be returned by the query function.
12+
* @param {object} params - The parameters for configuring the API query.
13+
* @param {string} params.name - The unique name for the query. This name, along with the optional `explicitKey`, forms the cache key for the query's result.
14+
* @param {Function} params.queryFn - The function to fetch data from the API. It receives a configured instance of `restApi` and must return a promise that resolves to the data of type `T`.
15+
* @param {string} [params.errorMessage] - Optional. A custom error message to be displayed in case the query fails. This message overrides the default error handling behavior.
16+
* @returns {UseQueryResult<T>} The result object from the useQuery hook, containing data and state information about the query (e.g., isLoading, isError).
1317
*/
14-
const useJetpackApiQuery = < T >(
15-
name: string,
16-
queryFn: ( api: typeof restApi ) => Promise< T >,
17-
explicitKey?: string,
18-
errorMessage?: string
19-
) => {
18+
type QueryParams< T > = {
19+
name: string;
20+
queryFn: ( api: typeof restApi ) => Promise< T >;
21+
errorMessage?: string;
22+
};
23+
const useJetpackApiQuery = < T >( { name, queryFn, errorMessage }: QueryParams< T > ) => {
2024
const queryResult = useQuery( {
21-
queryKey: [ name, explicitKey ],
25+
queryKey: [ name ],
2226
queryFn: () => {
2327
const { apiRoot, apiNonce } = window?.myJetpackRest || {};
2428
restApi.setApiRoot( apiRoot );

projects/packages/my-jetpack/_inc/data/use-simple-mutation.ts

+21-15
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,32 @@
11
import { useMutation } from '@tanstack/react-query';
22
import apiFetch from '@wordpress/api-fetch';
33
import { useFetchingErrorNotice } from './notices/use-fetching-error-notice';
4-
import type { UseMutationOptions } from '@tanstack/react-query';
4+
import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
55
import type { APIFetchOptions } from '@wordpress/api-fetch';
66

7-
/*
8-
* Simple wrapper for useQuery that handles error notices.
7+
/**
8+
* Executes a mutation with the specified parameters and options. This hook is designed
9+
* for performing data modification operations (e.g., POST, PUT, DELETE requests) and handling
10+
* the mutation's lifecycle events, such as success or failure. Additionally, it can display
11+
* an error notice if the mutation encounters an error.
912
*
10-
* This query is meant for any methods that include updating data (e.g. POST or DELETE), if you need to use a GET request, use useSimpleQuery.
11-
*
12-
* The options object is optional and is a strictly defined subset of the UseMutationOptions type.
13-
* If you want to pass more options, you can add them to the options type above.
13+
* @template T The type of data expected to be returned by the mutation.
14+
* @param {object} params - The parameters for executing the mutation.
15+
* @param {string} params.name - A unique name for the mutation, used as part of the mutation key.
16+
* @param {APIFetchOptions} params.query - The options to be passed to the API fetch function for the mutation.
17+
* @param {Pick<UseMutationOptions, 'onSuccess'>} [params.options] - Optional. Mutation options from react-query, currently supports only the 'onSuccess' option.
18+
* @param {string} [params.errorMessage] - Optional. A custom error message that can be displayed if the mutation fails.
19+
* @returns {UseMutationResult<T>} The result object from the useMutation hook, containing data and state information about the mutation (e.g., isPending, isError).
1420
*/
15-
const useSimpleMutation = < T >(
16-
name: string,
17-
query: APIFetchOptions,
18-
options?: Pick< UseMutationOptions, 'onSuccess' >,
19-
explicitKey?: string,
20-
errorMessage?: string
21-
) => {
21+
type QueryParams = {
22+
name: string;
23+
query: APIFetchOptions;
24+
options?: Pick< UseMutationOptions, 'onSuccess' >;
25+
errorMessage?: string;
26+
};
27+
const useSimpleMutation = < T >( { name, query, options, errorMessage }: QueryParams ) => {
2228
const mutationResult = useMutation< T >( {
23-
mutationKey: [ name, explicitKey ],
29+
mutationKey: [ name ],
2430
mutationFn: () => apiFetch< T >( query ),
2531
...options,
2632
} );

0 commit comments

Comments
 (0)