curi icon indicating copy to clipboard operation
curi copied to clipboard

(WIP) Suspense

Open pshrmn opened this issue 6 years ago • 3 comments

This is a work in progress to determine the API that should be used with React suspense

Prepping Curi to be ready for React Suspense. With Suspense, a navigation cannot be completed until after it has rendered.

When a router is created with the suspend: true option, the emitted navigation object will have a finish() function attached to it. The actual navigation (updating of the history) will not happen until that function is called.

const router = curi(history, routes, {
  suspend: true
});

router.respond(({ navigation }) => {
  // typeof navigation.finish === "function"
})

There are two changes that need to be done in React.

  1. Add the suspend boolean prop to the <CuriProvider>. When suspend is true, the provider will use deferred updates (not yet implemented because the API doesn't exist!) to update the state with the new response/navigation.
<CuriProvider router={router} suspend={true}>
  {({ response }) => {
    const { body:Body } = response;
    return <Body />;
  }}
</CuriProvider>
  1. Render a <FinishNavigation> inside of a <React.Placeholder>. The <FinishNavigation> component will call navigation.finish() for you once it mounts (or updates). By placing it inside of a <React.Placeholder>, we ensure that the componentDidMount()/componentDidUpdate() triggers are not called until any suspense code has loaded.

The placeholder's fallback component also needs to be wrapped in a <FinishNavigation> because when the fallback is displayed, the app is essentially rendering a spinner for the next page.

import { FinishNavigation } from "@curi/react";

const User = ({ response }) => (
  <React.Placeholder
    fallback={
      <FinishNavigation>
        <Spinner />
      </FinishNavigation>
    }
  >
    <FinishNavigation>
      <UserContent id={response.params.id} />
    </FinishNavigation>
  </React.Placeholder>
);

The API here is a little verbose, but a component that combines <React.Placeholder> and <FinishNavigation> should help.

import React from "react";
import { FinishNavigation } from "@curi/react";

const NavigationPlaceholder = ({ children, delayMs, fallback }) => (
  <React.Placeholder
    delayMs={delayMs}
    fallback={<FinishNavigation>{fallback}</FinishNavigation>}
  >
    <FinishNavigation>
      {children}
    </FinishNavigation>
  </React.Placeholder>
);

const User = ({ response }) => (
  <NavigationPlaceholder delayMs={2000} fallback={<Spinner />}>
    <UserContent id={response.params.id} />
  </NavigationPlaceholder>
);

pshrmn avatar Aug 23 '18 07:08 pshrmn

any idea if you could publish this as a separate alpha?

mfolnovic avatar May 10 '19 08:05 mfolnovic

@mfolnovic I'm getting close to releasing v2, at which point I can get this rebased (or at this point it might actually be easier to just copy things into a new PR) and push out a build.

I haven't been paying much attention to React's Suspense development; are they getting closer to releasing suspense for data or is it still only lazy loading?

pshrmn avatar May 10 '19 14:05 pshrmn

@pshrmn cool, tnx for info - this PR is closest to real support for Suspense (data fetching) in router from what I tried (and I tried react router, reach router, navi). :)

I haven't been paying much attention to React's Suspense development; are they getting closer to releasing suspense for data or is it still only lazy loading?

As far as I know, they aren't any news regarding suspense for data fetching. It's still planned for ~mid 2019 [1].

[1] https://reactjs.org/blog/2018/11/27/react-16-roadmap.html

mfolnovic avatar May 10 '19 19:05 mfolnovic