Caching Queries
BobaBoard’s frontend uses React Query as its query caching mechanism. See our knowledge base for basic information on caches.
Manual Cache Operations
Section titled “Manual Cache Operations”React Query automatically manages the caching and refetching of queries. That said, manual operations are required on occasion:
- Optimistic Updates: we
manually write the updated values in the existing cache after receiving new
data from the user. This gives the illusion of an immediate update while we
wait for server confirmation.
- Example: showing the updated board description in the sidebar as soon as the “save” button is clicked.
- Data Preloading: to preload the (potentially partial) result of a query
when partial data exists in already-loaded ones.
- Example: showing the first post in a thread (loaded from the board feed data) while waiting for the full thread to load.
Where to Define Manual Cache Operations
Section titled “Where to Define Manual Cache Operations”All caches operation are definined in the /cache directory. Each entity (e.g.
thread, board) should have its own file named as the entity (singular).
Getter and Setters
Section titled “Getter and Setters”The cache methods should use the following patterns:
get[entity]InCache: to retrieve entity data.set[entity]InCache: to update entity data.add[entity]InCache: to add a new entity data.
The Transformer Pattern
Section titled “The Transformer Pattern”Most often update operations have to be repeated by multiple functions across multiple caches. To ensure all functions consistently update an entity across all caches it appears in, we use a transformer pattern:
const set[entity]InCache = ({ queryClient: QueryClient, { entityId }: { entityId: string }, transform: (oldEntity: EntityType) => EntityType}) => { // Find all instance of "entity" in all caches by using the given id. [...]
// Get the updated value of the old entity const newEntity = transform(oldEntity); if (newEntity !== oldEntity) { // If transformer returns an updated entity, update the caches with the new values }}Our update methods can then use different transformers to update the data:
const setBoardMutedInCache = ({ queryClient: QueryClient, { boardId }: { boardId: string }) => { setBoardInCache({ queryClient, {boardId}, // The transformer (board: Board) => { if (board.muted) { // The board is already muted, so we return the same object. return board; } // We return a *new* object with the same property as board, but // the "muted" property set to true. return { ...board, muted: true; } } }) }const setBoardVisibleInCache = ({ queryClient: QueryClient, { boardId }: { boardId: string }) => { setBoardInCache({ queryClient, {boardId}, // The transformer (board: Board) => { if (!board.hidden) { // The board is already visible, so we return the same object. return board; } // We return a *new* object with the same property as board, but // the "hidden" property set to false. return { ...board, hidden: false; } } }) }Useful React Query Methods
Section titled “Useful React Query Methods”If you’re working with the cache, it might be useful to familiarize yourself with the following methods:
The best way to understand how to use them is to look at the existing
implementations of set[Entity]InCachecache methods.