Thanks! I have thought about this approach, too!
I am quite happy right now with my hooks approach. I was using a simple useQuery wrapper, that would use the QUERY variable of the Cell. But I changed to an approach with Context Provider. This gives me safety in consumption of hooks, since they need to be within the provider. - I think this approch did not come to my mind, since react context is not very performant. But I implemented it now with zustand. Zustand context is already much more performant than usage of useQuery. I will share the code - could be easily adapted to a custom generator for cells, too.
Context
createCellContext.tsx
import { createContext, PropsWithChildren, useContext, useRef, useLayoutEffect } from 'react'
import { createStore, useStore } from 'zustand'
/**
* zustand context
* https://docs.pmnd.rs/zustand/guides/initialize-state-with-props
* */
export function createCellContext<T extends any>() {
interface CellContextProps {
data: T
}
type CellContextStore = ReturnType<typeof createCellContextStore>
const createCellContextStore = (initProps: CellContextProps) => {
return createStore<CellContextProps>()((set) => ({
...initProps
}))
}
const CellContext = createContext<CellContextStore | null>(null)
type CellContextProviderProps = PropsWithChildren<CellContextProps>
function CellContextProvider({ children, ...props }: CellContextProviderProps) {
const storeRef = useRef<CellContextStore>(createCellContextStore(props))
if (!storeRef.current) {
storeRef.current = createCellContextStore(props)
}
useLayoutEffect(()=>{
// update store, if props change
storeRef.current.setState({data: props.data})
},[props])
return <CellContext.Provider value={storeRef.current}>
{children}
</CellContext.Provider>
}
function useCellContext() {
const store = useContext(CellContext)
if (!store) throw new Error('Missing CellContextProvider in the tree')
const storeData = useStore(store, (state) => state.data)
return storeData
}
return {
CellContextProvider,
useCellContext
}
}
Cell
And here consumption in Cell
TenantCell.tsx
import type { FindTenantById } from 'types/graphql'
import type { CellFailureProps, CellSuccessProps } from '@redwoodjs/web'
import { PropsWithChildren } from 'react'
import { createCellContext } from 'src/utils/createCellContext'
const { CellContextProvider, useCellContext } = createCellContext<FindTenantById['tenant']>()
export const QUERY = gql`
query FindTenantById($id: String!) {
tenant: tenant(id: $id) {
id
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>Tenant not found</div>
export const Failure = ({ error }: CellFailureProps) => (
<div className="rw-cell-error">{error?.message}</div>
)
export const Success = ({ tenant, children }:
PropsWithChildren<CellSuccessProps<FindTenantById>>) => {
return <CellContextProvider data={tenant}>
{children}
</CellContextProvider>
}
export const useTenantCell = useCellContext