@nacelle/react-hooks
Version:
Custom convenience hooks for use when building apps with Nacelle
246 lines (184 loc) • 7.91 kB
Markdown
# @nacelle/react-hooks
> React Hooks for Nacelle-fueled storefronts
## Install
```bash
npm i @nacelle/react-hooks
```
## Using the Hooks
### `useCheckout`
Use a checkout provider to create and manage checkouts.
#### Setting Up the Provider
Because checkout data and checkout actions should exist across the app, the `<CheckoutProvider />` needs to surround the root component of the application. The provider requires a `checkoutClient` prop, which is given an instance of a checkout client such as [`@nacelle/shopify-checkout`](https://www.npmjs.com/package/@nacelle/shopify-checkout). For example:
```jsx
import { CheckoutProvider } from '@nacelle/react-hooks';
import createShopifyCheckoutClient from '@nacelle/shopify-checkout';
const shopifyCheckoutClient = createShopifyCheckoutClient({
// for more info, see https://www.npmjs.com/package/@nacelle/shopify-checkout
storefrontCheckoutToken: process.env.SHOPIFY_STOREFRONT_CHECKOUT_TOKEN,
myshopifyDomain: process.env.MYSHOPIFY_DOMAIN,
storefrontApiVersion: process.env.STOREFRONT_API_VERSION
});
const App = () => {
<CheckoutProvider checkoutClient={shopifyCheckoutClient}>
<main>...</main>
</CheckoutProvider>;
};
```
You're welcome to create your own checkout client. It just needs to expose two methods:
```ts
function get(
params: any
): Promise<{
id: string;
url: string;
completed: boolean;
} | void> {
try {
// fetch an existing checkout
} catch (err) {
throw new Error(err);
}
}
function process(
params: any
): Promise<{
id: string;
url: string;
completed: boolean;
} | void> {
try {
// create a new checkout or update an existing checkout
} catch (err) {
throw new Error(err);
}
}
```
#### The `useCheckout` Hook
##### Importing
```js
import { useCheckout } from '@nacelle/react-hooks';
```
##### Accepts
(None)
##### Returns
An array containing:
1. `checkoutData`: an object containing the following properties:
- `completed` (boolean) - signals whether the checkout process has successfully completed
- `id` (string) - the checkout ID
- `url` (string) - the URL of the checkout page
- `getCheckoutError` (string) - error returned from the checkout client when fetching an existing checkout
- `processCheckoutError` (string) - error returned from the checkout client when processing a checkout
2. `checkoutActions`: an object containing the following properties:
- `processCheckout(params)`: (function) initiates checkout processing
- Accepts parameters required by the checkout client's `process` method
- Either rejects with an error message, or returns a promise which resolves to an object containing the `completed`, `id`, and `url`
- `getCheckout(params)` (function) updates the `checkoutData` (note: `useCheckout` runs this function automatically - it is provided only to satisfy special use cases)
- Accepts parameters required by the checkout client's `get` method
- Either rejects with an error message, or returns a promise which resolves to an object containing the `completed`, `id`, and `url`
- `clearCheckoutData`: (function) when called, resets the `checkoutData` and clears `checkoutData` values stored in browser storage
- Accepts: N/A
- Returns: N/A
3. `isCheckingOut`: a boolean that indicates whether or not the `processCheckout` function is currently running
##### Example Usage
```jsx
// Using the useCheckout hook
//
// This example assumes that you're creating a Shopify
// checkout with `@nacelle/shopify-checkout. For more info,
// see https://www.npmjs.com/package/@nacelle/shopify-checkout.
//
// If using a different checkout client, adjust the `processCheckout`
// params accordingly.
import { useCheckout } from '@nacelle/react-hooks';
const Cart = () => {
const [checkoutData, checkoutActions, isCheckingOut] = useCheckout();
const cartItems = [
{ variantId: 123456789, quantity: 1 },
{ variantId: 987654321, quantity: 4 }
];
useEffect(() => {
if (checkoutData.completed) {
checkoutActions.clearCheckoutData();
// any other post-checkout actions, such as clearing the cart
}
}, [checkoutData.completed, clearCheckoutData]);
const processCheckout = async () => {
await checkoutActions.processCheckout({ cartItems }).then(() => {
window.location = checkoutData.url;
});
};
return (
<>
<h2>Cart</h2>
<button type="button" disabled={isCheckingOut} onClick={processCheckout}>
{isLoading ? <>Loading...</> : <>Checkout</>}
</button>
</>
);
};
```
### `useCart`
A hook which uses items in a cart to generate a checkout via Nacelle's Hail Frequency API.
#### Setting Up the Provider
Because the cart should exist across the app, it needs to be setup to surround the root component of the application.
```jsx
import { CartProvider } from '@nacelle/react-hooks';
const App = () => {
<CartProvider>
<main>...</main>
</CartProvider>;
};
```
##### Cart Persistence
By default, the `<CartProvider />` uses [Local Storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to persist the cart between refreshes. If you would prefer to use [Session Storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) instead of Local Storage, pass `storage={'session'}` as a prop. If you would prefer to use [Index DB Storage](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) instead, pass `storage={'idb'}` as a prop. To disable cart storage entirely, pass `storage={null}` as a prop.
When using Local Storage or Session Storage to perist the cart, the default storage key of `'cart'` can be overriden by supplying a `cacheKey="my-custom-cart=key"` prop.
#### The `useCart` Hook
##### Importing
```js
import { useCart } from '@nacelle/react-hooks';
```
##### Accepts
(None)
##### Returns
An array containing:
1. `cartState`: an object containing the current state of the cart. The properties of this object are:
- `cart`: an array of line items in the cart
- `show`: a boolean to determine if the cart should be shown
2. `cartActions`: an object containing methods for interacting with the cart:
- `initCart(items)` - initialize/override the cart with an array of cart items
- `addToCart(item)` - add an item to the cart; if the item is already in the cart this function will increase the quantity of that item
- `updateItem(item)` - modify properties of an item in the cart
- `removeFromCart(item)` - remove an item from the cart
- `incrementItem(item)` - increment the quantity of an item in the cart
- `decrementItem(item)` - decrement the quantity of an item in the cart
- `toggleCart()` - toggles the cart's show status
- Optionally, accepts one of the following string arguments:
- `'open'` - if `cartState.show` was `false`, it will change to `true`; if `cartState.show` was `true`, it will remain `true`
- `'closed'` - if `cartState.show` was `false`, it will remain `false`; if `cartState.show` was `true`, it will change to `false`
- `clearCart()` - removes all items from the cart
3. `isInCart` - a helper function that determines if a line item is in the cart, via `isInCart(cart, item)`. If a custom `isInCart` function is given to the `<CartProvider />` via the `isInCart` prop, the custom function will be returned instead of the default `isInCart` function.
##### Example Usage
```jsx
// Using the useCart hook
import { useCart } from '@nacelle/react-hooks';
const Cart = () => {
const [{ cart }, { clearCart }] = useCart();
return (
<>
<h2>Cart</h2>
<button onClick={clearCart}>Clear</button>
<ul>
{cart.map((item) => (
<li key={item.id}>
<h3>{item.title}</h3>
<span>{item.quantity}</span>
<span>{item.price}</span>
</li>
))}
</ul>
</>
);
};
```
## License
ISC © [getnacelle](https://github.com/getnacelle)