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

remove mount check for react18 #1927

Merged
merged 1 commit into from Apr 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 29 additions & 18 deletions src/use-swr.ts
Expand Up @@ -178,14 +178,27 @@ export const useSWRHandler = <Data = any, Error = any>(
// new request should be initiated.
const shouldStartNewRequest = !FETCH[key] || !opts.dedupe

// Do unmount check for calls:
// If key has changed during the revalidation, or the component has been
// unmounted, old dispatch and old event callbacks should not take any
// effect.
const isCurrentKeyMounted = () =>
!unmountedRef.current &&
key === keyRef.current &&
initialMountedRef.current
/*
For React 17
Do unmount check for calls:
If key has changed during the revalidation, or the component has been
unmounted, old dispatch and old event callbacks should not take any
effect

For React 18
only check if key has changed
https://github.com/reactwg/react-18/discussions/82
*/
const callbackSafeguard = () => {
if (IS_REACT_LEGACY) {
return (
!unmountedRef.current &&
key === keyRef.current &&
initialMountedRef.current
)
}
return key === keyRef.current
}

// The final state object when request finishes.
const finalState: State<Data, Error> = {
Expand All @@ -197,7 +210,7 @@ export const useSWRHandler = <Data = any, Error = any>(
setCache(finalState)

// We can only set the local state if it's safe (still mounted with the same key).
if (isCurrentKeyMounted()) {
if (callbackSafeguard()) {
setState(finalState)
}
}
Expand Down Expand Up @@ -231,7 +244,7 @@ export const useSWRHandler = <Data = any, Error = any>(
// we trigger the loading slow event.
if (config.loadingTimeout && isUndefined(getCache().data)) {
setTimeout(() => {
if (loading && isCurrentKeyMounted()) {
if (loading && callbackSafeguard()) {
getConfig().onLoadingSlow(key, config)
}
}, config.loadingTimeout)
Expand Down Expand Up @@ -264,7 +277,7 @@ export const useSWRHandler = <Data = any, Error = any>(
// The timestamp maybe be `undefined` or a number
if (!FETCH[key] || FETCH[key][1] !== startAt) {
if (shouldStartNewRequest) {
if (isCurrentKeyMounted()) {
if (callbackSafeguard()) {
getConfig().onDiscarded(key)
}
}
Expand Down Expand Up @@ -298,7 +311,7 @@ export const useSWRHandler = <Data = any, Error = any>(
) {
finishRequestAndUpdateState()
if (shouldStartNewRequest) {
if (isCurrentKeyMounted()) {
if (callbackSafeguard()) {
getConfig().onDiscarded(key)
}
}
Expand All @@ -319,7 +332,7 @@ export const useSWRHandler = <Data = any, Error = any>(

// Trigger the successful callback if it's the original request.
if (shouldStartNewRequest) {
if (isCurrentKeyMounted()) {
if (callbackSafeguard()) {
getConfig().onSuccess(newData, key, config)
}
}
Expand All @@ -336,7 +349,7 @@ export const useSWRHandler = <Data = any, Error = any>(

// Error event and retry logic. Only for the actual request, not
// deduped ones.
if (shouldStartNewRequest && isCurrentKeyMounted()) {
if (shouldStartNewRequest && callbackSafeguard()) {
currentConfig.onError(err, key, currentConfig)
if (
shouldRetryOnError === true ||
Expand Down Expand Up @@ -371,7 +384,7 @@ export const useSWRHandler = <Data = any, Error = any>(

// Here is the source of the request, need to tell all other hooks to
// update their states.
if (isCurrentKeyMounted() && shouldStartNewRequest) {
if (callbackSafeguard() && shouldStartNewRequest) {
broadcastState(cache, key, finalState)
}

Expand Down Expand Up @@ -551,9 +564,7 @@ export const useSWRHandler = <Data = any, Error = any>(
// without providing any initial data. See:
// https://github.com/vercel/swr/issues/1832
if (!IS_REACT_LEGACY && IS_SERVER) {
throw new Error(
'Fallback data is required when using suspense in SSR.'
)
throw new Error('Fallback data is required when using suspense in SSR.')
}

// Always update fetcher and config refs even with the Suspense mode.
Expand Down
47 changes: 24 additions & 23 deletions src/utils/broadcast-state.ts
@@ -1,5 +1,5 @@
import { Broadcaster } from '../types'
import { SWRGlobalState, GlobalState } from './global-state'
import { SWRGlobalState } from './global-state'
import * as revalidateEvents from '../constants'
import { createCacheHelper } from './cache'

Expand All @@ -10,33 +10,34 @@ export const broadcastState: Broadcaster = (
revalidate,
broadcast = true
) => {
const [EVENT_REVALIDATORS, STATE_UPDATERS, , FETCH] = SWRGlobalState.get(
cache
) as GlobalState
const revalidators = EVENT_REVALIDATORS[key]
const updaters = STATE_UPDATERS[key]
const stateResult = SWRGlobalState.get(cache)
if (stateResult) {
const [EVENT_REVALIDATORS, STATE_UPDATERS, , FETCH] = stateResult
const revalidators = EVENT_REVALIDATORS[key]
const updaters = STATE_UPDATERS[key]

const [get] = createCacheHelper(cache, key)
const [get] = createCacheHelper(cache, key)

// Cache was populated, update states of all hooks.
if (broadcast && updaters) {
for (let i = 0; i < updaters.length; ++i) {
updaters[i](state)
// Cache was populated, update states of all hooks.
if (broadcast && updaters) {
for (let i = 0; i < updaters.length; ++i) {
updaters[i](state)
}
}
}

// If we also need to revalidate, only do it for the first hook.
if (revalidate) {
// Invalidate the key by deleting the concurrent request markers so new
// requests will not be deduped.
delete FETCH[key]
// If we also need to revalidate, only do it for the first hook.
if (revalidate) {
// Invalidate the key by deleting the concurrent request markers so new
// requests will not be deduped.
delete FETCH[key]

if (revalidators && revalidators[0]) {
return revalidators[0](revalidateEvents.MUTATE_EVENT).then(
() => get().data
)
if (revalidators && revalidators[0]) {
return revalidators[0](revalidateEvents.MUTATE_EVENT).then(
() => get().data
)
}
}
}

return get().data
return get().data
}
}
4 changes: 3 additions & 1 deletion src/utils/env.ts
@@ -1,7 +1,9 @@
import React, { useEffect, useLayoutEffect } from 'react'
import { hasRequestAnimationFrame, isWindowDefined } from './helper'

export const IS_REACT_LEGACY = !(React as any).useId
// @ts-expect-error TODO: should remove this when the default react version is 18
export const IS_REACT_LEGACY = !React.useId

export const IS_SERVER = !isWindowDefined || 'Deno' in window

// Polyfill requestAnimationFrame
Expand Down