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

feat: add preload function #2026

Merged
merged 17 commits into from Jun 17, 2022
Merged

feat: add preload function #2026

merged 17 commits into from Jun 17, 2022

Conversation

koba04
Copy link
Collaborator

@koba04 koba04 commented Jun 12, 2022

This is an experimental implementation to preload fetcher functions. The following is an example.

import useSWR, { preload } from 'swr'

// before rendering
preload('/api/user', fetcher);

const Page = () => {
  const { data } = useSWR('/api/user', fetcher);
  return <div>...</div>
}

The preload function is necessary to avoid waterfall problems with the suspense mode.
Currently, this is implemented as middleware, but the API might be changed before merging.

The limitation of the current implementation is that we have to use useSWR from swr/preload.
This is added as a built-in middleware

The following has the waterfall problem.

import useSWR, { preload } from 'swr'

const Page = () => {
  // this suspends the first rendering
  const { data: user } = useSWR('/api/user', fetcher, { suspense: true });
  // this start fetching only after `/api/user` has been fetched
  const { data: movies } = useSWR('/api/movies', fetcher, { suspense: true });
  return <div>...</div>
}

To avoid that, we should preload fetcher functions. preload is the function for it.

import useSWR, { preload } from 'swr'

// before rendering
preload('/api/user', fetcher);
preload('/api/movies', fetcher);

const Page = () => {
  // These fetcher functions have been already fetched by calling `prefetch`.
  const { data: user } = useSWR('/api/user', fetcher, { suspense: true });
  const { data: movies } = useSWR('/api/movies', fetcher, { suspense: true });
  return <div>...</div>
}

@codesandbox-ci
Copy link

codesandbox-ci bot commented Jun 12, 2022

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 85f160c:

Sandbox Source
SWR-Basic Configuration
SWR-States Configuration
SWR-Infinite Configuration
SWR-SSR Configuration

@koba04 koba04 changed the title feat: swr/preload WIP: swr/preload Jun 12, 2022
@shuding
Copy link
Member

shuding commented Jun 13, 2022

I think we can try building this into the default useSWR hook (import useSWR from 'swr'), maybe make it a default middleware that is always included here: https://github.com/vercel/swr/blob/main/_internal/utils/resolve-args.ts#L19-L25

@koba04
Copy link
Collaborator Author

koba04 commented Jun 13, 2022

@shuding Thank you! That makes sense, I'll try it 👍

@koba04
Copy link
Collaborator Author

koba04 commented Jun 14, 2022

I've renamed the API from prefetch to preload.

const fetcher =
fetcher_ == null
? null
: // fetcher might be a sync function, so this should not be an async function
Copy link
Collaborator Author

@koba04 koba04 Jun 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this becomes an async function, the following test is failed.

it('should reset isValidating when an error occured synchronously', async () => {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense!

@koba04
Copy link
Collaborator Author

koba04 commented Jun 14, 2022

I've added a test for resetting an error when preloading has been failed.

@koba04 koba04 changed the title WIP: swr/preload WIP: feat: preload Jun 15, 2022
@koba04 koba04 changed the title WIP: feat: preload feat: preload Jun 15, 2022
@koba04 koba04 changed the title feat: preload feat: add preload function Jun 15, 2022
@koba04 koba04 marked this pull request as ready for review June 15, 2022 15:50
@koba04 koba04 requested a review from huozhi as a code owner June 15, 2022 15:50
import { Middleware, Key, BareFetcher } from '../types'
import { serialize } from './serialize'

const REQUEST = new Map<string, Promise<any>>()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be bound with cache in global state?

Copy link
Collaborator Author

@koba04 koba04 Jun 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@huozhi Thank you! That makes sense to me.
But does the preload function have to accept cache as the argument to use the global state? (It might work if we always use a global cache to store the value)

// we need the cache argument to get REQUEST from SWRGlobalState
preload(key, fetcher, cache) 

Copy link
Member

@shuding shuding Jun 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's OK to make it always global and apply to any cache layers for now (just like fallback) considering the major use cases of preload. If necessary we can add an optional cache parameter in the future, that wouldn't be breaking.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've used SWRGlobalState to store preloading requests.
85f160c

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this API. I wanted to confirm, does it use custom cache if provider config is set?

_internal/utils/resolve-args.ts Outdated Show resolved Hide resolved
_internal/utils/preload.ts Outdated Show resolved Hide resolved
_internal/utils/preload.ts Outdated Show resolved Hide resolved
Copy link
Member

@huozhi huozhi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good on my side

@shuding shuding merged commit 371dae3 into vercel:main Jun 17, 2022
@shuding
Copy link
Member

shuding commented Jun 17, 2022

Looks amazing, thank you!

@koba04
Copy link
Collaborator Author

koba04 commented Jun 17, 2022

Thank you!

@koba04 koba04 deleted the feat-preload branch June 17, 2022 13:13
@koba04 koba04 mentioned this pull request Aug 5, 2022
13 tasks
@zelal-dev
Copy link

Great..!! It looks amazing, best wishes!

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

Successfully merging this pull request may close these issues.

None yet

5 participants