@salesforce/commerce-sdk-react 1.0.0-preview.2 → 1.0.0-preview.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -3
- package/README.md +59 -52
- package/hooks/useEncUserId.d.ts +8 -1
- package/hooks/useEncUserId.js +13 -2
- package/hooks/useQuery.js +7 -2
- package/hooks/useUsid.d.ts +8 -1
- package/hooks/useUsid.js +17 -9
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -5,12 +5,12 @@ A collection of <a href="https://tanstack.com/query/latest/docs/react/overview">
|
|
|
5
5
|
|
|
6
6
|
## 🎯 Features
|
|
7
7
|
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
- Shopper authentication & token management via [SLAS](https://developer.salesforce.com/docs/commerce/commerce-api/references/shopper-login)
|
|
9
|
+
- Server side data fetching (in conjuction with PWA Kit)
|
|
10
|
+
- Phased Launch support ([plugin_slas](https://github.com/SalesforceCommerceCloud/plugin_slas) compatible)
|
|
11
|
+
- Built-in caching for easy state management
|
|
12
|
+
- automatic cache invalidations/updates via the library's built-in mutations
|
|
13
|
+
- automatic cache key generation
|
|
14
14
|
|
|
15
15
|
## ⚙️ Installation
|
|
16
16
|
|
|
@@ -43,10 +43,11 @@ const AppConfig = ({children}) => {
|
|
|
43
43
|
{children}
|
|
44
44
|
</CommerceApiProvider>
|
|
45
45
|
)
|
|
46
|
-
}
|
|
46
|
+
}
|
|
47
47
|
|
|
48
48
|
// Set configuration options for react query.
|
|
49
49
|
// NOTE: This configuration will be used both on the server-side and client-side.
|
|
50
|
+
// retry is always disabled on server side regardless of the value from the options
|
|
50
51
|
const options = {
|
|
51
52
|
queryClientConfig: {
|
|
52
53
|
defaultOptions: {
|
|
@@ -65,13 +66,12 @@ export default withReactQuery(AppConfig, options)
|
|
|
65
66
|
|
|
66
67
|
## ⚡️ Quickstart (Generic React App)
|
|
67
68
|
|
|
68
|
-
You can use this library in any React application by creating a new QueryClient and wrap your application with `QueryClientProvider`. For example:
|
|
69
|
+
You can use this library in any React application by creating a new QueryClient and wrap your application with `QueryClientProvider`. For example:
|
|
69
70
|
|
|
70
71
|
```jsx
|
|
71
72
|
import {CommerceApiProvider} from '@salesforce/commerce-sdk-react'
|
|
72
73
|
import {QueryClient, QueryClientProvider} from '@tanstack/react-query'
|
|
73
74
|
|
|
74
|
-
|
|
75
75
|
const App = ({children}) => {
|
|
76
76
|
const queryClient = new QueryClient()
|
|
77
77
|
|
|
@@ -91,7 +91,7 @@ const App = ({children}) => {
|
|
|
91
91
|
</CommerceApiProvider>
|
|
92
92
|
</QueryClientProvider>
|
|
93
93
|
)
|
|
94
|
-
}
|
|
94
|
+
}
|
|
95
95
|
|
|
96
96
|
export default App
|
|
97
97
|
```
|
|
@@ -104,7 +104,7 @@ To help reduce boilerplate code for managing shopper authentication, by default,
|
|
|
104
104
|
|
|
105
105
|
### Shopper Session Initialization
|
|
106
106
|
|
|
107
|
-
On `CommerceApiProvider` mount, the provider initializes shopper session by initiating the SLAS
|
|
107
|
+
On `CommerceApiProvider` mount, the provider initializes shopper session by initiating the SLAS **Public Client** login flow. The tokens are stored/managed/refreshed by the library.
|
|
108
108
|
|
|
109
109
|
### Authenticate request queue
|
|
110
110
|
|
|
@@ -136,19 +136,15 @@ You have the option of handling shopper authentication externally, by providing
|
|
|
136
136
|
|
|
137
137
|
```jsx
|
|
138
138
|
const MyComponent = ({children}) => {
|
|
139
|
-
return
|
|
140
|
-
|
|
141
|
-
{children}
|
|
142
|
-
</CommerceApiProvider>
|
|
143
|
-
)
|
|
144
|
-
}
|
|
139
|
+
return <CommerceApiProvider fetchedToken="xxxxxxxxxxxx">{children}</CommerceApiProvider>
|
|
140
|
+
}
|
|
145
141
|
```
|
|
146
142
|
|
|
147
143
|
## Hooks
|
|
148
144
|
|
|
149
145
|
The majority of hooks provided in this library are built on top of the [useQuery](https://tanstack.com/query/latest/docs/react/reference/useQuery) and the [useMutation](https://tanstack.com/query/latest/docs/react/reference/useMutation) hook from [react-query](https://tanstack.com/query/latest). React-query provides a declarative way for fetching and updating data. This library takes advantage of the features provided by react-query and combine with the [commerce-sdk-isomorphic](https://github.com/SalesforceCommerceCloud/commerce-sdk-isomorphic) API client to create a collection of hooks to simplify data fetching for SCAPI.
|
|
150
146
|
|
|
151
|
-
The hooks can be categorized into
|
|
147
|
+
The hooks can be categorized into **Query hooks** and **Mutation hooks**.
|
|
152
148
|
|
|
153
149
|
### Query hooks
|
|
154
150
|
|
|
@@ -158,7 +154,7 @@ The query hooks correspond to the http GET endpoints from the SCAPI. The query h
|
|
|
158
154
|
use<EntityName>(CommerceClientOptions, ReactQueryOptions)
|
|
159
155
|
```
|
|
160
156
|
|
|
161
|
-
Both the
|
|
157
|
+
Both the **required** and **optional** parameters for the underlying `commerce-sdk-isomorphic` call is passed as the first parameter:
|
|
162
158
|
|
|
163
159
|
```jsx
|
|
164
160
|
import {useProduct} from '@salesforce/commerce-sdk-react'
|
|
@@ -171,10 +167,12 @@ const Example = () => {
|
|
|
171
167
|
}
|
|
172
168
|
})
|
|
173
169
|
|
|
174
|
-
return
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
170
|
+
return (
|
|
171
|
+
<>
|
|
172
|
+
<p>isLoading: {query.isLoading}</p>
|
|
173
|
+
<p>name: {query.data?.name}</p>
|
|
174
|
+
</>
|
|
175
|
+
)
|
|
178
176
|
}
|
|
179
177
|
```
|
|
180
178
|
|
|
@@ -186,15 +184,18 @@ import {useBasket} from '@salesforce/commerce-sdk-react'
|
|
|
186
184
|
const onServer = typeof window === undefined
|
|
187
185
|
|
|
188
186
|
const Example = ({basketId}) => {
|
|
189
|
-
const query = useBasket(
|
|
190
|
-
|
|
191
|
-
|
|
187
|
+
const query = useBasket(
|
|
188
|
+
{
|
|
189
|
+
parameters: {
|
|
190
|
+
basketId: basketId
|
|
191
|
+
}
|
|
192
192
|
},
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
193
|
+
{
|
|
194
|
+
// A common use case for `enabled` is
|
|
195
|
+
// to conditionally fetch based on environment
|
|
196
|
+
enabled: !onServer && basketId
|
|
197
|
+
}
|
|
198
|
+
)
|
|
198
199
|
}
|
|
199
200
|
```
|
|
200
201
|
|
|
@@ -215,16 +216,22 @@ const Example = ({basketId}) => {
|
|
|
215
216
|
// Typescript IDE intellisense for available options
|
|
216
217
|
const addItemToBasket = useShopperBasketsMutation('addItemToBasket')
|
|
217
218
|
|
|
218
|
-
return
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
219
|
+
return (
|
|
220
|
+
<button
|
|
221
|
+
onClick={() =>
|
|
222
|
+
addItemToBasket.mutate({
|
|
223
|
+
parameters: {
|
|
224
|
+
basketId
|
|
225
|
+
},
|
|
226
|
+
body: {
|
|
227
|
+
productId: '25592770M',
|
|
228
|
+
price: 55,
|
|
229
|
+
quantity: 1
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
}
|
|
233
|
+
/>
|
|
234
|
+
)
|
|
228
235
|
}
|
|
229
236
|
```
|
|
230
237
|
|
|
@@ -247,7 +254,7 @@ const Example = ({basketId}) => {
|
|
|
247
254
|
|
|
248
255
|
Since mutations changes data on the server, the cache entries that are potentially affected by the mutation is automatically invalidated.
|
|
249
256
|
|
|
250
|
-
For example, an `addItemToBasket` mutation automatically update `useBasket`
|
|
257
|
+
For example, an `addItemToBasket` mutation automatically update `useBasket` and `useCustomerBaskets` query cache, because the mutation result contains the information for the updated basket. In other cases, when the mutation response do not have the updated data, the library will invalidate the cache and trigger a re-fetch. For the DELETE endpoints, the library removes the cache entries on successful mutations.
|
|
251
258
|
|
|
252
259
|
_💡 Debugging hint: install and include `@tanstack/react-query-devtools` in your React app to see the queries (inspect the query states and cache keys)._
|
|
253
260
|
|
|
@@ -269,14 +276,13 @@ const Example = () => {
|
|
|
269
276
|
const fetchProducts = async () => {
|
|
270
277
|
const token = await getTokenWhenReady()
|
|
271
278
|
const products = await api.shopperProducts.getProducts({
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
279
|
+
parameters: {ids: ids.join(',')},
|
|
280
|
+
headers: {
|
|
281
|
+
Authorization: `Bearer ${token}`
|
|
282
|
+
}
|
|
283
|
+
})
|
|
277
284
|
return products
|
|
278
285
|
}
|
|
279
|
-
|
|
280
286
|
}
|
|
281
287
|
```
|
|
282
288
|
|
|
@@ -303,18 +309,19 @@ useCustomerId() => null | 'guest' | 'registered'
|
|
|
303
309
|
### `useEncUserId()`
|
|
304
310
|
|
|
305
311
|
```ts
|
|
306
|
-
useEncUserId() =>
|
|
312
|
+
useEncUserId() => {encUserId: String, getEncUserIdWhenReady: Promise}
|
|
307
313
|
```
|
|
308
314
|
|
|
309
315
|
### `useUsid()`
|
|
310
316
|
|
|
311
317
|
```ts
|
|
312
|
-
useUsid() =>
|
|
318
|
+
useUsid() => {usid: String, getUsidWhenReady: Promise}
|
|
313
319
|
```
|
|
314
320
|
|
|
315
321
|
## Roadmap
|
|
316
|
-
|
|
317
|
-
-
|
|
322
|
+
|
|
323
|
+
- Optimistic update support
|
|
324
|
+
- SLAS private client support
|
|
318
325
|
|
|
319
326
|
## Useful Links:
|
|
320
327
|
|
package/hooks/useEncUserId.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @group Shopper Authentication helpers
|
|
3
|
+
*/
|
|
4
|
+
interface EncUserId {
|
|
5
|
+
encUserId: string | null;
|
|
6
|
+
getEncUserIdWhenReady: () => Promise<string>;
|
|
7
|
+
}
|
|
1
8
|
/**
|
|
2
9
|
* Hook that returns the ecom user ID.
|
|
3
10
|
*
|
|
@@ -7,6 +14,6 @@
|
|
|
7
14
|
* @category Shopper Authentication
|
|
8
15
|
*
|
|
9
16
|
*/
|
|
10
|
-
declare const useEncUserId: () =>
|
|
17
|
+
declare const useEncUserId: () => EncUserId;
|
|
11
18
|
export default useEncUserId;
|
|
12
19
|
//# sourceMappingURL=useEncUserId.d.ts.map
|
package/hooks/useEncUserId.js
CHANGED
|
@@ -17,6 +17,10 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
17
17
|
|
|
18
18
|
const onClient = typeof window !== 'undefined';
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* @group Shopper Authentication helpers
|
|
22
|
+
*/
|
|
23
|
+
|
|
20
24
|
/**
|
|
21
25
|
* Hook that returns the ecom user ID.
|
|
22
26
|
*
|
|
@@ -29,11 +33,18 @@ const onClient = typeof window !== 'undefined';
|
|
|
29
33
|
const useEncUserId = () => {
|
|
30
34
|
const config = (0, _useConfig.default)();
|
|
31
35
|
const auth = (0, _useAuthContext.default)();
|
|
32
|
-
|
|
36
|
+
const encUserId = onClient ?
|
|
33
37
|
// This conditional is a constant value based on the environment, so the same path will
|
|
34
38
|
// always be followed., and the "rule of hooks" is not violated.
|
|
35
39
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
36
|
-
(0, _useLocalStorage.default)(
|
|
40
|
+
(0, _useLocalStorage.default)(`enc_user_id_${config.siteId}`) : auth.get('enc_user_id');
|
|
41
|
+
const getEncUserIdWhenReady = () => auth.ready().then(({
|
|
42
|
+
enc_user_id
|
|
43
|
+
}) => enc_user_id);
|
|
44
|
+
return {
|
|
45
|
+
encUserId,
|
|
46
|
+
getEncUserIdWhenReady
|
|
47
|
+
};
|
|
37
48
|
};
|
|
38
49
|
var _default = useEncUserId;
|
|
39
50
|
exports.default = _default;
|
package/hooks/useQuery.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.useQuery = void 0;
|
|
|
7
7
|
var _reactQuery = require("@tanstack/react-query");
|
|
8
8
|
var _useAuthorizationHeader = require("./useAuthorizationHeader");
|
|
9
9
|
var _utils = require("./utils");
|
|
10
|
+
var _utils2 = require("../utils");
|
|
10
11
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
11
12
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
12
13
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
@@ -45,13 +46,17 @@ const useQuery = (apiOptions, queryOptions, hookConfig) => {
|
|
|
45
46
|
return _ref.apply(this, arguments);
|
|
46
47
|
};
|
|
47
48
|
}();
|
|
48
|
-
return (0, _reactQuery.useQuery)(hookConfig.queryKey, wrappedMethod, _objectSpread({
|
|
49
|
+
return (0, _reactQuery.useQuery)(hookConfig.queryKey, wrappedMethod, _objectSpread(_objectSpread(_objectSpread({
|
|
49
50
|
enabled:
|
|
50
51
|
// Individual hooks can provide `enabled` checks that are done in ADDITION to
|
|
51
52
|
// the required parameter check
|
|
52
53
|
hookConfig.enabled !== false &&
|
|
53
54
|
// The default `enabled` is "has all required parameters"
|
|
54
55
|
(0, _utils.hasAllKeys)(apiOptions.parameters, hookConfig.requiredParameters)
|
|
55
|
-
}, queryOptions)
|
|
56
|
+
}, queryOptions), queryOptions !== null && queryOptions !== void 0 && queryOptions.retry ? {
|
|
57
|
+
retry: (0, _utils2.onClient)() ? queryOptions === null || queryOptions === void 0 ? void 0 : queryOptions.retry : false
|
|
58
|
+
} : {}), queryOptions !== null && queryOptions !== void 0 && queryOptions.retryOnMount ? {
|
|
59
|
+
retryOnMount: (0, _utils2.onClient)() ? queryOptions === null || queryOptions === void 0 ? void 0 : queryOptions.retryOnMount : false
|
|
60
|
+
} : {}));
|
|
56
61
|
};
|
|
57
62
|
exports.useQuery = useQuery;
|
package/hooks/useUsid.d.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @group Shopper Authentication helpers
|
|
3
|
+
*/
|
|
4
|
+
interface Usid {
|
|
5
|
+
usid: string | null;
|
|
6
|
+
getUsidWhenReady: () => Promise<string>;
|
|
7
|
+
}
|
|
1
8
|
/**
|
|
2
9
|
* Hook that returns the usid associated with the current access token.
|
|
3
10
|
*
|
|
4
11
|
* @group Helpers
|
|
5
12
|
* @category Shopper Authentication
|
|
6
13
|
*/
|
|
7
|
-
declare const useUsid: () =>
|
|
14
|
+
declare const useUsid: () => Usid;
|
|
8
15
|
export default useUsid;
|
|
9
16
|
//# sourceMappingURL=useUsid.d.ts.map
|
package/hooks/useUsid.js
CHANGED
|
@@ -5,8 +5,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _useAuthContext = _interopRequireDefault(require("./useAuthContext"));
|
|
8
|
-
var _useLocalStorage = _interopRequireDefault(require("./useLocalStorage"));
|
|
9
|
-
var _useConfig = _interopRequireDefault(require("./useConfig"));
|
|
10
8
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
9
|
/*
|
|
12
10
|
* Copyright (c) 2022, salesforce.com, inc.
|
|
@@ -15,7 +13,9 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
15
13
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
16
14
|
*/
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
/**
|
|
17
|
+
* @group Shopper Authentication helpers
|
|
18
|
+
*/
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Hook that returns the usid associated with the current access token.
|
|
@@ -24,13 +24,21 @@ const onClient = typeof window !== 'undefined';
|
|
|
24
24
|
* @category Shopper Authentication
|
|
25
25
|
*/
|
|
26
26
|
const useUsid = () => {
|
|
27
|
-
const config = (0, _useConfig.default)();
|
|
28
27
|
const auth = (0, _useAuthContext.default)();
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
|
|
28
|
+
|
|
29
|
+
// TODO: auth.get does not trigger a re-render.
|
|
30
|
+
// This is fine for now since the only time the usid changes is on logout
|
|
31
|
+
// and currently when we log out we redirect to the login page which
|
|
32
|
+
// causes components to unmount.
|
|
33
|
+
// This will need to change if we stay on the PDP after logout
|
|
34
|
+
const usid = auth.get('usid');
|
|
35
|
+
const getUsidWhenReady = () => auth.ready().then(({
|
|
36
|
+
usid
|
|
37
|
+
}) => usid);
|
|
38
|
+
return {
|
|
39
|
+
usid,
|
|
40
|
+
getUsidWhenReady
|
|
41
|
+
};
|
|
34
42
|
};
|
|
35
43
|
var _default = useUsid;
|
|
36
44
|
exports.default = _default;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/commerce-sdk-react",
|
|
3
|
-
"version": "1.0.0-preview.
|
|
3
|
+
"version": "1.0.0-preview.4",
|
|
4
4
|
"description": "A library that provides react hooks for fetching data from Commerce Cloud",
|
|
5
5
|
"homepage": "https://github.com/SalesforceCommerceCloud/pwa-kit/tree/develop/packages/ecom-react-hooks#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"typedoc-plugin-missing-exports": "^2.0.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@salesforce/pwa-kit-dev": "3.0.0-preview.
|
|
50
|
+
"@salesforce/pwa-kit-dev": "3.0.0-preview.4",
|
|
51
51
|
"@tanstack/react-query": "^4.28.0",
|
|
52
52
|
"@testing-library/jest-dom": "^5.16.5",
|
|
53
53
|
"@testing-library/react": "^14.0.0",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"@types/react-dom": "^18.2.1",
|
|
60
60
|
"@types/react-helmet": "^6.1.6",
|
|
61
61
|
"cross-env": "^5.2.1",
|
|
62
|
-
"internal-lib-build": "3.0.0-preview.
|
|
62
|
+
"internal-lib-build": "3.0.0-preview.4",
|
|
63
63
|
"jsonwebtoken": "^9.0.0",
|
|
64
64
|
"nock": "^13.3.0",
|
|
65
65
|
"nodemon": "^2.0.22",
|
|
@@ -86,5 +86,5 @@
|
|
|
86
86
|
"publishConfig": {
|
|
87
87
|
"directory": "dist"
|
|
88
88
|
},
|
|
89
|
-
"gitHead": "
|
|
89
|
+
"gitHead": "06b3f17fd233777136fdb699aa93df4c52874217"
|
|
90
90
|
}
|