Skip to content

Commit

Permalink
share revalidation logic between session and cart
Browse files Browse the repository at this point in the history
  • Loading branch information
tlgimenes committed May 26, 2022
1 parent 9228ab3 commit 3c078ed
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 76 deletions.
6 changes: 3 additions & 3 deletions packages/api/src/platforms/vtex/resolvers/validateSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export const validateSession = async (
clients.commerce.session(params.toString()).catch(() => null),
])

const profile = sessionData?.namespaces.profile
const store = sessionData?.namespaces.store
const profile = sessionData?.namespaces.profile ?? null
const store = sessionData?.namespaces.store ?? null

const newSession = {
...oldSession,
Expand All @@ -41,7 +41,7 @@ export const validateSession = async (
salesChannel: store?.channel?.value ?? channel.salesChannel,
regionId: regionData?.[0]?.id ?? channel.regionId,
}),
person: profile
person: profile?.id
? {
id: profile.id?.value ?? '',
email: profile.email?.value ?? '',
Expand Down
48 changes: 10 additions & 38 deletions packages/sdk/src/cart/Optimistic.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { createContext, useEffect, useMemo, useState } from 'react'
import React, { createContext, useMemo } from 'react'
import type { PropsWithChildren } from 'react'

import { useContext } from '../utils/useContext'
import { createUseValidationHook } from '../utils/useValidation'
import { Context as CartContext } from './Cart'
import type { ContextValue as CartContextValue, Item } from './Cart'

Expand All @@ -14,49 +15,20 @@ export interface Props<T extends Item> {
export const Context = createContext<boolean | undefined>(undefined)
Context.displayName = 'StoreCartValidatorContext'

const nullable = async () => null

// Validation queue
let queue = Promise.resolve()
const useValidation = createUseValidationHook()

export const OptimisticProvider = <T extends Item = Item>({
children,
onValidateCart = nullable,
onValidateCart,
}: PropsWithChildren<Props<T>>) => {
const { items, id, setCart } = useContext(CartContext)
const cart = useMemo(() => ({ id, items }), [id, items])
const [isValidating, setIsValidating] = useState(false)

useEffect(() => {
let cancel = false

const revalidate = async () => {
if (cancel) {
return
}

setIsValidating(true)
const newCart = await onValidateCart(cart as Cart<T>)

if (cancel) {
return
}

setIsValidating(false)
if (newCart != null) {
setCart(newCart)
}
}

// Enqueue validation
setTimeout(() => {
queue = queue.then(revalidate)
}, 0)

return () => {
cancel = true
}
}, [cart, onValidateCart, setCart])

const isValidating = useValidation({
onValidate: onValidateCart,
value: cart as Cart<T>,
setValue: setCart,
})

return <Context.Provider value={isValidating}>{children}</Context.Provider>
}
50 changes: 17 additions & 33 deletions packages/sdk/src/session/Revalidate.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,35 @@
import React, { useEffect } from 'react'
import React, { createContext, useMemo } from 'react'
import type { PropsWithChildren } from 'react'

import { useContext } from '../utils/useContext'
import { Context } from './Session'
import { createUseValidationHook } from '../utils/useValidation'
import { Context as SessionContext } from './Session'
import type { Session } from './Session'

export interface Props {
onValidateSession?: (session: Session) => Promise<Session | null>
}

// Validation queue
let queue = Promise.resolve()
export const Context = createContext<boolean | undefined>(undefined)

const useValidation = createUseValidationHook()

export const RevalidateProvider = ({
onValidateSession,
children,
}: PropsWithChildren<Props>) => {
const { setSession, ...session } = useContext(Context)

useEffect(() => {
let cancel = false

const revalidate = async () => {
if (cancel || onValidateSession == null) {
return
}

const newSession = await onValidateSession(session)

if (cancel) {
return
}

if (newSession != null) {
setSession(newSession)
}
}
const context = useContext(SessionContext)
const session = useMemo(() => {
const { setSession, ...rest } = context

// Enqueue validation
setTimeout(() => {
queue = queue.then(revalidate)
}, 0)
return rest
}, [context])

return () => {
cancel = true
}
}, [onValidateSession, session, setSession])
const isValidating = useValidation({
onValidate: onValidateSession,
value: session,
setValue: context.setSession,
})

return <>{children}</>
return <Context.Provider value={isValidating}>{children}</Context.Provider>
}
13 changes: 11 additions & 2 deletions packages/sdk/src/session/useSession.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { Context } from './Session'
import { Context as SessionContext } from './Session'
import { Context as ValidationContext } from './Revalidate'
import { useContext } from '../utils/useContext'

export const useSession = () => useContext(Context)
export const useSession = () => {
const session = useContext(SessionContext)
const isValidating = useContext(ValidationContext)

return {
...session,
isValidating,
}
}
57 changes: 57 additions & 0 deletions packages/sdk/src/utils/useValidation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useEffect, useState } from 'react'

interface Options<T> {
onValidate?: (value: T) => Promise<T | null>
value: T
setValue: (value: T) => void
}

const nullable = async () => null

export const createUseValidationHook = () => {
// Validation queue
let queue = Promise.resolve()

const useValidation = <T>({
onValidate = nullable,
value,
setValue,
}: Options<T>) => {
const [isValidating, setIsValidating] = useState(true)

useEffect(() => {
let cancel = false

const revalidate = async () => {
if (cancel) {
return
}

setIsValidating(true)
const newValue = await onValidate(value)

if (cancel) {
return
}

setIsValidating(false)
if (newValue != null) {
setValue(newValue)
}
}

// Enqueue validation
setTimeout(() => {
queue = queue.then(revalidate)
}, 0)

return () => {
cancel = true
}
}, [onValidate, setValue, value])

return isValidating
}

return useValidation
}

0 comments on commit 3c078ed

Please sign in to comment.