Skip to content

Commit 91e3e33

Browse files
committed
Minor suspense store refactor
1 parent 8e5fedd commit 91e3e33

File tree

1 file changed

+36
-11
lines changed

1 file changed

+36
-11
lines changed

src/React/Basic/Hooks/Suspense/Store.purs

+36-11
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,24 @@ import Prelude
99
import Control.Alt ((<|>))
1010
import Data.DateTime.Instant (Instant, unInstant)
1111
import Data.Either (Either(..))
12+
import Data.Int (ceil)
1213
import Data.Map (Map)
1314
import Data.Map as Map
1415
import Data.Maybe (Maybe(..))
15-
import Data.Time.Duration (Milliseconds)
16+
import Data.Newtype (un)
17+
import Data.Time.Duration (Milliseconds(..))
1618
import Effect (Effect)
1719
import Effect.Aff (Aff, attempt, launchAff, throwError)
1820
import Effect.Class (liftEffect)
21+
import Effect.Console (warn)
22+
import Effect.Exception (try)
1923
import Effect.Now (now)
2024
import Effect.Ref (Ref)
2125
import Effect.Ref as Ref
2226
import React.Basic.Hooks (type (/\), (/\))
2327
import React.Basic.Hooks.Suspense (Suspended(..), SuspenseResult(..))
28+
import Web.HTML (window)
29+
import Web.HTML.Window (requestIdleCallback)
2430

2531
-- | Simple key-based cache.
2632
mkSuspenseStore ::
@@ -32,19 +38,35 @@ mkSuspenseStore ::
3238
mkSuspenseStore defaultMaxAge backend = do
3339
ref <- Ref.new mempty
3440
let
41+
isExpired maxAge now' (_ /\ d) = unInstant now' < unInstant d <> maxAge
42+
43+
pruneCache = do
44+
case defaultMaxAge of
45+
Nothing -> pure unit
46+
Just maxAge -> do
47+
now' <- now
48+
void $ Ref.modify (Map.filter (not isExpired maxAge now')) ref
49+
void
50+
$ window
51+
>>= requestIdleCallback
52+
{ timeout: ceil $ un Milliseconds maxAge
53+
}
54+
pruneCache
55+
3556
tryFromCache itemMaxAge k = do
3657
rMaybe <- Map.lookup k <$> Ref.read ref
3758
case rMaybe of
3859
Nothing -> pure Nothing
39-
Just (r /\ d) -> do
60+
Just v@(r /\ d) -> do
4061
case itemMaxAge <|> defaultMaxAge of
4162
Nothing -> pure (Just r)
4263
Just maxAge -> do
4364
now' <- now
44-
if unInstant now' < unInstant d <> maxAge then
45-
pure (Just r)
46-
else
65+
if isExpired maxAge now' v then do
66+
_ <- Ref.modify (Map.delete k) ref
4767
pure Nothing
68+
else
69+
pure (Just r)
4870

4971
getCacheOrBackend itemMaxAge k = do
5072
c <- tryFromCache itemMaxAge k
@@ -80,22 +102,25 @@ mkSuspenseStore defaultMaxAge backend = do
80102
d <- now
81103
_ <- ref # Ref.modify (Map.insert k (v /\ d))
82104
pure v
105+
do
106+
r <- try pruneCache
107+
case r of
108+
Left _ -> warn "Failed to initialize the suspense store cleanup task. Ensure you're using it in a browser with `requestIdleCallback` support."
109+
Right _ -> pure unit
83110
pure
84111
$ SuspenseStore
85112
{ cache: ref
86-
, get: \k -> Suspended do getCacheOrBackend Nothing k
87-
, get': \d k -> Suspended do getCacheOrBackend (Just d) k
113+
, get: map Suspended <<< getCacheOrBackend
88114
}
89115

90116
newtype SuspenseStore k v
91117
= SuspenseStore
92118
{ cache :: Ref (Map k (SuspenseResult v /\ Instant))
93-
, get :: k -> Suspended v
94-
, get' :: Milliseconds -> k -> Suspended v
119+
, get :: Maybe Milliseconds -> k -> Suspended v
95120
}
96121

97122
get :: forall k v. SuspenseStore k v -> k -> Suspended v
98-
get (SuspenseStore s) = s.get
123+
get (SuspenseStore s) = s.get Nothing
99124

100125
get' :: forall k v. SuspenseStore k v -> Milliseconds -> k -> Suspended v
101-
get' (SuspenseStore s) = s.get'
126+
get' (SuspenseStore s) d = s.get (Just d)

0 commit comments

Comments
 (0)