Skip to content

Commit

Permalink
remove mount check for react18 (#1927)
Browse files Browse the repository at this point in the history
  • Loading branch information
promer94 committed Apr 20, 2022
1 parent 56f99ca commit c0e0d99
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 42 deletions.
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

0 comments on commit c0e0d99

Please sign in to comment.