Skip to content

Commit

Permalink
remove mount check for react18
Browse files Browse the repository at this point in the history
  • Loading branch information
promer94 committed Apr 17, 2022
1 parent 2dda58a commit 963e914
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 40 deletions.
46 changes: 30 additions & 16 deletions src/use-swr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
UNDEFINED,
OBJECT,
mergeObjects,
isFunction
isFunction,
IS_REACT_LEGACY
} from './utils/helper'
import ConfigProvider from './utils/config-context'
import { useStateWithDeps } from './utils/state'
Expand Down Expand Up @@ -173,14 +174,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 @@ -192,7 +206,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 @@ -226,7 +240,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 @@ -259,7 +273,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 @@ -293,7 +307,7 @@ export const useSWRHandler = <Data = any, Error = any>(
) {
finishRequestAndUpdateState()
if (shouldStartNewRequest) {
if (isCurrentKeyMounted()) {
if (callbackSafeguard()) {
getConfig().onDiscarded(key)
}
}
Expand All @@ -314,7 +328,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 @@ -331,7 +345,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 @@ -366,7 +380,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
47 changes: 24 additions & 23 deletions src/utils/broadcast-state.ts
Original file line number Diff line number Diff line change
@@ -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
}
}
3 changes: 2 additions & 1 deletion src/utils/helper.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { version } from 'react'
export const noop = () => {}

// Using noop() as the undefined value as undefined can possibly be replaced
// by something else. Prettier ignore and extra parentheses are necessary here
// to ensure that tsc doesn't remove the __NOINLINE__ comment.
// prettier-ignore
export const UNDEFINED = (/*#__NOINLINE__*/ noop()) as undefined

export const IS_REACT_LEGACY = !version.startsWith('18')
export const OBJECT = Object

export const isUndefined = (v: any): v is undefined => v === UNDEFINED
Expand Down

0 comments on commit 963e914

Please sign in to comment.