#ReOpened : Saving deferred serverLoader response to localforage

1 messages · Page 1 of 1 (latest)

naive jay
#

https://www.youtube.com/watch?v=fuwm0BBNkRY

Hi, guys.
I've watched the latest Ryan's tutorial video above.
At 3:53, He checked loader function wasn`t deferred.
What if the loader was deferred, is there any way to save the completed response to localforage?

Take fine-grained control of your site's user experience by leveraging clientLoader to cache data directly in the browser.

Play with this demo 👉 https://remix-movies.pages.dev/
Checkout the code 👉 https://github.com/remix-run/example-movies

00:00 - Previous caching strategies overview
00:46 - Client-side caching with browser storage
04:45 - De...

▶ Play video
shut glacier
#

So you could either chain a .then on the lazy data to put it into localforage, or you could await it. It does get a little weird when it comes to making sure the cached data is displayed right away - just something to keep in mind.

#

Someone recently shared an interesting technique for doing this kind of SWR with deferred data and clientLoader #react-router-contributing message

#

Oh, yeah. Assume that getLazyData returns a promise.

naive jay
#

@shut glacier
Thank you, Alex!
I appreciated your such a quick response😃

Just for making sure, is my understanding is correct?
In the case above, the order of resolving might be like this?

#

loader

clientLoader runs & component render

lazydata completed

completed lazyData appeals in component with Suspence & Await

shut glacier
#

Correct.

#

(Depending on whether you've set clientLoader.hydrate and whether it's a document request or a client-side navigation)

naive jay
#

I see.

Thank you a lot!!

naive jay
#

I created the sample code below.
Why my serverLoader runs every time I reload?
I thought serverLoader will run only first time.

import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
import { LoaderFunction, json } from "@remix-run/node";
import {
  Link,
  useLoaderData,
  ClientLoaderFunctionArgs,
  defer,
  Await,
} from "@remix-run/react";
import { Suspense } from "react";
import localforage from "localforage";

interface LazyData {
  message: string;
}

export const loader = async ({ request }: LoaderFunctionArgs) => {
  console.log("loader function ran");
  return defer({
    message: getLazyData(),
  });
};

export async function clientLoader({ serverLoader }: ClientLoaderFunctionArgs) {
  console.log("clientLoader function ran");
  let cacheKey = "lazy-data";
  let cached = await localforage.getItem<LazyData>(cacheKey);
  if (cached) {
    return { message: cached };
  }

  let data: LazyData = await serverLoader();
  let cacheData = data.message;
  await localforage.setItem(cacheKey, cacheData);
  return cacheData;
}

clientLoader.hydrate = true;

export default function Index() {
  const data = useLoaderData<typeof loader>();
  console.log("Index function ran");
  console.log(data);
  return (
    <div className="h-full flex flex-col items-center pt-20 bg-slate-900 text-white">
      <p>Playground Page</p>
      <Suspense fallback={<Loading />}>
        <Await resolve={data.message}>
          {(resolvedData) => {
            const { message } = resolvedData;
            return (
              <div>
                <p>{message}</p>
              </div>
            );
          }}
        </Await>
      </Suspense>
    </div>
  );
}

function getLazyData() {
  return new Promise<LazyData>((resolve) => {
    setTimeout(() => {
      resolve({ message: "Hello from a lazy loader!" });
    }, 1000);
  });
}

function Loading() {
  return <div>Loading...</div>;
}

naive jay
#

ReOpened : Saving deferred serverLoader response to localforage

half jolt
#

If you do a hard reload, the server loader needs to run to generate the initial SSR document

naive jay
#

@half jolt
I did nomral reload. but I still runs the server loader...

half jolt
#

Right, like clicking the browser 🔃 button? That triggers a fresh document request to the server

naive jay
#

Oh that’s bad. Is there any way to prepend server side request when I reload?

half jolt
#

Why is that bad? That's just how HTTP/browsers work

#

If you never want to run that logic on initial load until you've checked for a client side cache - then you really want client side rendering for that route. I would:

  • Move the server loader to a new resource route
  • Add a HydrateFallback to render on the server
  • Then just make a raw fetch to the resource route in clientLoader when the cached data is not available
#

But that's quite different from the pattern Ryan is demonstrating in the video above.