
BSMNT Commerce Toolkit 에 오신 것을 환영합니다. 패키지는 더 나은 상점을 배송하고 더 빠르게 배송 할 수 있도록 도와줍니다.
이 툴킷은 우리 (Basement.studio)를 지원했습니다. 그중 일부에는 ShopMrbeast.com, Karljacobs.co, Shopmrballen.com 및 Ranboo.fashion이 포함됩니다.
Next.js + Shopify와 함께 예제를 찾고 있다면 여기에서 예제를 확인하십시오.
이 저장소는 현재 3 개의 패키지를 보유하고 있습니다.
@bsmnt/storefront-hooks : 스토어 프론트 클라이언트 측 상태를 관리하기위한 반응 후크.
@tanstack/react-query 및 localStorage 의 도움으로 전체 카트 라이프 사이클 관리 @bsmnt/sdk-gen : 유형-안전, GraphQL SDK를 생성하는 CLI.
graphql 에 의존하지 않기 때문에 avarage보다 가벼워 @bsmnt/drop : 카운트 다운 관리를위한 도우미. 일반적으로 상품 드롭 주위에 과대 광고를 만드는 데 사용됩니다.
이것들은 정말 잘 재생되지만 별도로 사용할 수도 있습니다. 그들이 어떻게 작동하는지 봅시다!
@bsmnt/storefront-hooks yarn add @bsmnt/storefront-hooks @tanstack/react-query이 패키지 수출 :
createStorefrontHooks : 카트와 상호 작용하는 데 필요한 후크를 만드는 기능 . import { createStorefrontHooks } from '@bsmnt/storefront-hooks'
export const hooks = createStorefrontHooks ( {
cartCookieKey : '' , // to save cart id in cookie
fetchers : { } , // hooks will use these internally
mutators : { } , // hooks will use these internally
createCartIfNotFound : false , // defaults to false. if true, will create a cart if none is found
queryClientConfig : { } // internal query client config
} )몇 가지 예를 살펴보십시오.
localStorage 와 함께 간단한 예 import { createStorefrontHooks } from '@bsmnt/storefront-hooks'
type LineItem = {
merchandiseId : string
quantity : number
}
type Cart = {
id : string
lines : LineItem [ ]
}
export const {
QueryClientProvider ,
useCartQuery ,
useAddLineItemsToCartMutation ,
useOptimisticCartUpdate ,
useRemoveLineItemsFromCartMutation ,
useUpdateLineItemsInCartMutation
} = createStorefrontHooks < Cart > ( {
cartCookieKey : 'example-nextjs-localstorage' ,
fetchers : {
fetchCart : ( cartId : string ) => {
const cartFromLocalStorage = localStorage . getItem ( cartId )
if ( ! cartFromLocalStorage ) throw new Error ( 'Cart not found' )
const cart : Cart = JSON . parse ( cartFromLocalStorage )
return cart
}
} ,
mutators : {
addLineItemsToCart : ( cartId , lines ) => {
const cartFromLocalStorage = localStorage . getItem ( cartId )
if ( ! cartFromLocalStorage ) throw new Error ( 'Cart not found' )
const cart : Cart = JSON . parse ( cartFromLocalStorage )
// Add line if not exists, update quantity if exists
const updatedCart = lines . reduce ( ( cart , line ) => {
const lineIndex = cart . lines . findIndex (
( cartLine ) => cartLine . merchandiseId === line . merchandiseId
)
if ( lineIndex === - 1 ) {
cart . lines . push ( line )
} else {
cart . lines [ lineIndex ] ! . quantity += line . quantity
}
return cart
} , cart )
localStorage . setItem ( cartId , JSON . stringify ( updatedCart ) )
return {
data : updatedCart
}
} ,
createCart : ( ) => {
const cart : Cart = { id : 'cart' , lines : [ ] }
localStorage . setItem ( cart . id , JSON . stringify ( cart ) )
return { data : cart }
} ,
createCartWithLines : ( lines ) => {
const cart = { id : 'cart' , lines }
localStorage . setItem ( cart . id , JSON . stringify ( cart ) )
return { data : cart }
} ,
removeLineItemsFromCart : ( cartId , lineIds ) => {
const cartFromLocalStorage = localStorage . getItem ( cartId )
if ( ! cartFromLocalStorage ) throw new Error ( 'Cart not found' )
const cart : Cart = JSON . parse ( cartFromLocalStorage )
cart . lines = cart . lines . filter (
( line ) => ! lineIds . includes ( line . merchandiseId )
)
localStorage . setItem ( cart . id , JSON . stringify ( cart ) )
return {
data : cart
}
} ,
updateLineItemsInCart : ( cartId , lines ) => {
const cartFromLocalStorage = localStorage . getItem ( cartId )
if ( ! cartFromLocalStorage ) throw new Error ( 'Cart not found' )
const cart : Cart = JSON . parse ( cartFromLocalStorage )
cart . lines = lines
localStorage . setItem ( cart . id , JSON . stringify ( cart ) )
return {
data : cart
}
}
} ,
logging : {
onError ( type , error ) {
console . info ( { type , error } )
} ,
onSuccess ( type , data ) {
console . info ( { type , data } )
}
}
} )@bsmnt/sdk-gen 과 함께 완전한 예제 # Given the following file tree:
.
└── storefront/
├── sdk-gen/
│ └── sdk.ts # generated with @bsmnt/sdk-gen
└── hooks.ts # <- we'll work here이 예제는 @bsmnt/sdk-gen에 따라 다릅니다.
// ./storefront/hooks.ts
import { createStorefrontHooks } from '@bsmnt/storefront-hooks'
import { storefront } from '../sdk-gen/sdk'
import type {
CartGenqlSelection ,
CartUserErrorGenqlSelection ,
FieldsSelection ,
Cart as GenqlCart
} from '../sdk-gen/generated'
const cartFragment = {
id : true ,
checkoutUrl : true ,
createdAt : true ,
cost : { subtotalAmount : { amount : true , currencyCode : true } }
} satisfies CartGenqlSelection
export type Cart = FieldsSelection < GenqlCart , typeof cartFragment >
const userErrorFragment = {
message : true ,
code : true ,
field : true
} satisfies CartUserErrorGenqlSelection
export const {
QueryClientProvider ,
useCartQuery ,
useAddLineItemsToCartMutation ,
useOptimisticCartUpdate ,
useRemoveLineItemsFromCartMutation ,
useUpdateLineItemsInCartMutation
} = createStorefrontHooks ( {
cartCookieKey : 'example-nextjs-shopify' ,
fetchers : {
fetchCart : async ( cartId ) => {
const { cart } = await storefront . query ( {
cart : {
__args : { id : cartId } ,
... cartFragment
}
} )
if ( cart === undefined ) throw new Error ( 'Request failed' )
return cart
}
} ,
mutators : {
addLineItemsToCart : async ( cartId , lines ) => {
const { cartLinesAdd } = await storefront . mutation ( {
cartLinesAdd : {
__args : {
cartId ,
lines
} ,
cart : cartFragment ,
userErrors : userErrorFragment
}
} )
return {
data : cartLinesAdd ?. cart ,
userErrors : cartLinesAdd ?. userErrors
}
} ,
createCart : async ( ) => {
const { cartCreate } = await storefront . mutation ( {
cartCreate : {
cart : cartFragment ,
userErrors : userErrorFragment
}
} )
return {
data : cartCreate ?. cart ,
userErrors : cartCreate ?. userErrors
}
} ,
// TODO we could use the same mutation as createCart?
createCartWithLines : async ( lines ) => {
const { cartCreate } = await storefront . mutation ( {
cartCreate : {
__args : { input : { lines } } ,
cart : cartFragment ,
userErrors : userErrorFragment
}
} )
return {
data : cartCreate ?. cart ,
userErrors : cartCreate ?. userErrors
}
} ,
removeLineItemsFromCart : async ( cartId , lineIds ) => {
const { cartLinesRemove } = await storefront . mutation ( {
cartLinesRemove : {
__args : { cartId , lineIds } ,
cart : cartFragment ,
userErrors : userErrorFragment
}
} )
return {
data : cartLinesRemove ?. cart ,
userErrors : cartLinesRemove ?. userErrors
}
} ,
updateLineItemsInCart : async ( cartId , lines ) => {
const { cartLinesUpdate } = await storefront . mutation ( {
cartLinesUpdate : {
__args : {
cartId ,
lines : lines . map ( ( l ) => ( {
id : l . merchandiseId ,
quantity : l . quantity ,
attributes : l . attributes
} ) )
} ,
cart : cartFragment ,
userErrors : userErrorFragment
}
} )
return {
data : cartLinesUpdate ?. cart ,
userErrors : cartLinesUpdate ?. userErrors
}
}
} ,
createCartIfNotFound : true
} ) @bsmnt/sdk-gen yarn add @bsmnt/sdk-gen --dev 이 패키지는 단일 명령으로 CLI를 설치합니다. generate . 실행하면 GraphQL 엔드 포인트에 도달하고 쿼리 및 돌연변이에서 TypeScript 유형을 생성합니다. GenQL에 의해 구동되므로 문서를 확인하십시오.
# By default, you can have a file tree like the following:
.
└── sdk-gen/
└── config.js // ./sdk-gen/config.js
/**
* @type {import("@bsmnt/sdk-gen").Config}
*/
module . exports = {
endpoint : '' ,
headers : { }
}그런 다음 발전기를 실행할 수 있습니다.
yarn sdk-gen config.js 파일의 경우 ./sdk-gen/ 내부와 해당 디렉토리의 모든 .{graphql,gql} 파일에 대해 살펴 봅니다.
사용자 정의 디렉토리를 사용하려면 (기본값이 아닌 ./sdk-gen/ --dir 를 사용할 수 있습니다.
yarn sdk-gen --dir ./my-custom/directory발전기를 실행 한 후 다음 결과를 얻을 수 있습니다.
.
└── sdk-gen/
├── config.js
├── documents.gql
├── generated/ # <- generated
│ ├── index.ts
│ └── graphql.schema.json
└── sdk.ts # <- generated sdk.ts 에서 bsmntSdk 내보내게 할 것입니다.
import config from './config'
import { createSdk } from './generated'
export const bsmntSdk = createSdk ( config )그리고 그게 전부입니다. 이를 사용하여 안전한 유형의 그래프 QL API를 누르기 위해 사용할 수 있어야합니다.
추가적인 이점은이 SDK가 graphql 에 의존하지 않는다는 것입니다. 많은 GraphQL 클라이언트는이를 피어 의존성 (예 : graphql-request )으로 요구하므로 묶음에 중요한 KB를 추가합니다.
shopify Storefront API와 함께이를 사용하는 표준 방법은 Next.js + Shopify를 사용하여 예제를 살펴보십시오.
@bsmnt/drop yarn add @bsmnt/drop이 패키지 수출 :
CountdownProvider : CountdownStore 의 컨텍스트 제공 업체useCountdownStore : CountdownProvider 컨텍스트를 소비하고 CountdownStore 를 반환하는 후크zeroPad : 0을 제로로 채우는 유틸리티 사용하려면 카운트 다운을 추가하려는 곳마다 CountdownProvider 를 감싸십시오. 예를 들어 Next.js :
// _app.tsx
import type { AppProps } from 'next/app'
import { CountdownProvider } from '@bsmnt/drop'
import { Countdown } from '../components/countdown'
export default function App ( { Component , pageProps } : AppProps ) {
return (
< CountdownProvider
endDate = { Date . now ( ) + 1000 * 5 } // set this to 5 seconds from now just to test
countdownChildren = { < Countdown /> }
exitDelay = { 1000 } // optional, just to give some time to animate the countdown before finally unmounting it
startDate = { Date . now ( ) } // optional, just if you need some kind of progress UI
>
< Component { ... pageProps } />
</ CountdownProvider >
)
}그리고 카운트 다운은 다음과 같은 것처럼 보일 수 있습니다.
import { useCountdownStore } from '@bsmnt/drop'
export const Countdown = ( ) => {
const humanTimeRemaining = useCountdownStore ( ) (
( state ) => state . humanTimeRemaining // keep in mind this is zustand, so you can slice this store
)
return (
< div >
< h1 > Countdown </ h1 >
< ul >
< li > Days: { humanTimeRemaining . days } </ li >
< li > Hours: { humanTimeRemaining . hours } </ li >
< li > Minutes: { humanTimeRemaining . minutes } </ li >
< li > Seconds: { humanTimeRemaining . seconds } </ li >
</ ul >
</ div >
)
} humanTimeRemaining.seconds 렌더링하는 경우, 초, 서버가 클라이언트와 다른 것을 렌더링 할 가능성이 높습니다. 해당 값은 매 초마다 변경 될 것입니다.
대부분의 경우 안전하게 suppressHydrationWarning 할 수 있습니다 (자세한 내용은 21 호 참조) :
import { useCountdownStore } from '@bsmnt/drop'
export const Countdown = ( ) => {
const humanTimeRemaining = useCountdownStore ( ) (
( state ) => state . humanTimeRemaining // keep in mind this is zustand, so you can slice this store
)
return (
< div >
< h1 > Countdown </ h1 >
< ul >
< li suppressHydrationWarning > Days: { humanTimeRemaining . days } </ li >
< li suppressHydrationWarning > Hours: { humanTimeRemaining . hours } </ li >
< li suppressHydrationWarning > Minutes: { humanTimeRemaining . minutes } </ li >
< li suppressHydrationWarning > Seconds: { humanTimeRemaining . seconds } </ li >
</ ul >
</ div >
)
}그 위험을 감수하고 싶지 않다면, 나머지 시간을 렌더링하기 전에 앱이 수화 될 때까지 더 안전한 옵션이 기다리고 있습니다.
import { useEffect , useState } from 'react'
import { useCountdownStore } from '@bsmnt/drop'
const Countdown = ( ) => {
const humanTimeRemaining = useCountdownStore ( ) (
( state ) => state . humanTimeRemaining // keep in mind this is zustand, so you can slice this store
)
const [ hasRenderedOnce , setHasRenderedOnce ] = useState ( false )
useEffect ( ( ) => {
setHasRenderedOnce ( true )
} , [ ] )
return (
< div >
< h1 > Countdown </ h1 >
< ul >
< li > Days: { humanTimeRemaining . days } </ li >
< li > Hours: { humanTimeRemaining . hours } </ li >
< li > Minutes: { hasRenderedOnce ? humanTimeRemaining . minutes : '59' } </ li >
< li > Seconds: { hasRenderedOnce ? humanTimeRemaining . seconds : '59' } </ li >
</ ul >
</ div >
)
} 시작하기위한 몇 가지 예 :
localStorage 와 함께 풀 요청을 환영합니다. 문제를 환영합니다. 주요 변경 사항을 위해 먼저 문제를 열어 변경하고 싶은 것을 논의하십시오.
MIT