Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remix #248

Open
phryneas opened this issue Mar 22, 2024 · 4 comments
Open

Remix #248

phryneas opened this issue Mar 22, 2024 · 4 comments

Comments

@phryneas
Copy link
Member

First the good news: streaming SSR with the suspense hooks and Remix works.

All the setup for that is in this diff: phryneas/remix-apollo-stream-example@bc43efe

That said, supporting loaders is another beast.
I'll add my thoughts in this thread.

@phryneas
Copy link
Member Author

Thoughts on Loaders

Loaders run independently of React.

  • on a SSR request, they run before streaming SSR happens in handleRequest.
    • as I understand it, they don't even need to run in the same process/host
  • on later page navigation, they run on the server, but no more SSR happens

As a result, our current "inject into the stream" approach won't work - we have no way of sharing an AC instance reliably between handleRequest and loader, and oftentimes there's not even a handleRequest

=> each loader should create their own instance of Apollo Client and createQueryPreloader.

const loader = () => {
  const preload = createQueryPreloader(makeClient())

  const result1 = preload(query1)
  const result2 = preload(query2)

  return defer({
    result1,
    result2
  })
}

Now, right now preload creates a queryRef - that's hardly feasible in this case, since it can't be transported over the wire.

So we probably need to wrap createQueryPreloader (apollographql/apollo-client#11719) to have a completely different functionality in SSR.

It could create a value like

{
  query,
  variables,
  resultPromise
}

which would then be picked up by an (also wrapped) useReadQuery that would simulate an ongoing request (simulating the request start sooner would be great, but is probably not possible).

Currently, the defer implementation doesn't support promises that are not on the top level yet.

We'd probably need buy-in from the Remix team here to be able to make according changes on their side - once we have fleshed out this story more.

@LydiaF
Copy link

LydiaF commented Jul 21, 2024

Hi @phryneas,

I was trying to get the transported data stored locally to the cache and I came up with below... It does do what I intend it to but I am wondering if this is how you would go about it.

Thanks for any direction :)

// makeClient.ts
import { HttpLink } from "@apollo/client/index.js";
import { ApolloClient, InMemoryCache } from "@apollo/client-react-streaming";
import { CachePersistor } from 'apollo3-cache-persist';

export const cache = new InMemoryCache()

const persistor =
  typeof window !== 'undefined'
    ? new CachePersistor({
        cache,
        storage: localStorage,
      })
    : null;

persistor &&
  persistor.restore().then(() => {
    // console.log('cache restored')
  });


export function makeClient() {
  return new ApolloClient({
    cache: typeof window === 'undefined' ? new InMemoryCache() : cache,
    link: new HttpLink({
      uri: "https://swapi-graphql.netlify.app/.netlify/functions/index",
    }),
  });
}

@phryneas
Copy link
Member Author

@LydiaF It seems okay, although I would probably question if this is necessary at all - if you use this package to it's full extend, every page should bring their data to the cache anyways.

Also, you probably shouldn't be creating an ApolloClient outside of a makeClient function, there's a risk that you share data between different users in SSR if you do this on a module level.

I do wonder though, why are you posting this in the Remix issue? This doesn't seem to add to that topic.

@LydiaF
Copy link

LydiaF commented Jul 22, 2024

Hi @phryneas, thank you for replying so quickly and for your help. I think I am okay now.

I think I was confused because I was still thinking in terms of how I used to do things -- I would look in Application local storage to see the cache data (as opposed to Apollo dev tools), and would also typically import the cache to do something like below as opposed to using useApolloClient. So I was lost as to how to call methods on the cache and then wrongly thought the data was somehow not really there.

// app/routes/_index.tsx
export default function Index() {
  const client = useApolloClient();

  return (
    <div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.8" }}>
      <h1>Welcome to Remix</h1>
      <button
        onClick={() => {
          client.cache.updateQuery({ query: ALL_FILMS }, (data) => ({
            allFilms: {
              __typename: "FilmsConnection",
              films: [],
            },
          }));
        }}
      >
        Press Me
      </button>
      <Suspense fallback="loading">
        <Suspended />
      </Suspense>
    </div>
  );
}

As for posting here, apologies, I probably should have asked in the repo you made.

Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants