#React.cache() with object as parameter

38 messages · Page 1 of 1 (latest)

stoic pawn
#

I have this graphql client:

import { cache } from "react"

interface GraphqlRequestParameters<T, V> {
  document: RequestDocument | TypedDocumentNode<T, V>
  variables?: V
  withAuth?: boolean
  requestHeaders?: GraphQLClientRequestHeaders
  onError?: (error: unknown) => void
}

export async function graphqlServerRequest<T, V extends Variables = Variables>({
  document,
  variables,
  withAuth = false,
  requestHeaders,
  onError,
}: GraphqlRequestParameters<T, V>): Promise<T | undefined> {
  try {
    if (withAuth) {
      return await graphqlServerWithAuth.request(document, variables, requestHeaders)
    } else {
      return await graphqlServerWithoutAuth.request(document, variables, requestHeaders)
    }
  } catch (error) {
    if (onError) onError(error)
    else throw error
  }
}

export const cachedGraphqlServerRequest = cache(graphqlServerRequest)

The thing is that, when I call my function like this, it doesn't work (I can still see 2 request being triggered):

// page.tsx

  await cachedGraphqlServerRequest({
    document: someQuery,
    withAuth: true,
    variables: { slug },
    onError() {
      notFound()
    },
  })

  await cachedGraphqlServerRequest({
    document: someQuery,
    withAuth: true,
    variables: { slug },
    onError() {
      notFound()
    },
  })

But now if I do this, it works (only one request being triggered):

// page.tsx

  const param = {
    document: someQuery,
    withAuth: true,
    variables: { slug },
    onError() {
      notFound()
    },
  }

  await cachedGraphqlServerRequest(param)

  await cachedGraphqlServerRequest(param)

I know that now it works because the object reference is the same. The problem is that this example is quite simple but most of the time I will not be able to create an object before hand.

So is there a way to modify my cachedGraphqlServerRequest so I can keep calling my the function with the object as a parameter ?

old ridge
#

You could make your own cache if the inputs are seralizable

#

just store results in a map, and check for existance on each call

stoic pawn
#

Why do you mean with "my own cache" ? Like on top of the react cache ?

old ridge
#

You just create a map, and store results in it

#
const cache = new Map<string, string>()
function getCache(key: string) {
  if (cache.has(key)) return cache.get(key)
  const result = getResult(key)
  cache.set(key, result)
  return result
}
stoic pawn
#

Oh ok
But is it efficient ? I suppose that the React cache is more complex than that ?

old ridge
#

It's a basic programming technique : )

old ridge
#

It's very efficient

stoic pawn
#

No problem with the memory getting bloated ?

old ridge
#

Same as with React

#

Well, perhaps react clears the cache after some time?

stoic pawn
#

React cache is reset for each server request

old ridge
#

Oh then it's no problem

#

You have to think about where you initialize/store your cache though

#

and how you can access it

#

maybe use a Context

stoic pawn
#

mmh

Context are not usable server side

#

But I will think about a solution

old ridge
#

I haven't used server side functions

stoic pawn
#

No problem
Thank you for your help

old ridge
#

Can you initialise it somewhere and pass it through props?

#

Or do you have some client/server boundaries?

stoic pawn
#

client and server cannot share state

old ridge
#

Sure, but you only need this on the server

#

I presume

stoic pawn
#

yes

old ridge
#

If all your server side components are connected it should be OK. If you have a lot of islands, then I don't know.

#

Seems like something that there must be a solution for

stoic pawn
#

yep

old ridge
#

import { createServerContext } from 'react';

#

server context?

stoic pawn
#

never heard of

old ridge
#

I think you'll find something 😄

stoic pawn
#

Yep that's the inital help I needed ^^