@khanacademy/wonder-blocks-data 15.0.2 → 15.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @khanacademy/wonder-blocks-data
2
2
 
3
+ ## 15.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 0d4e856: Fix `useCachedEffect` refetching forever when an unmemoized `onResultChanged` callback is passed with the `CacheAndNetwork` or `NetworkOnly` fetch policies. The callback is now tracked via a latest-value ref so its identity no longer invalidates the memoized fetch function (which cancelled the inflight request and restarted the fetch); the most recently supplied callback is still the one invoked when a result arrives.
8
+
3
9
  ## 15.0.2
4
10
 
5
11
  ### Patch Changes
package/dist/es/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Server, useForceUpdate } from '@khanacademy/wonder-blocks-core';
1
+ import { Server, useForceUpdate, useLatestRef } from '@khanacademy/wonder-blocks-core';
2
2
  import { KindError, clone, entries } from '@khanacademy/wonder-stuff-core';
3
3
  import * as React from 'react';
4
4
  import { useContext, useRef, useMemo, useCallback } from 'react';
@@ -38,7 +38,7 @@ const useRequestInterception=(requestId,handler)=>{const interceptors=React.useC
38
38
 
39
39
  const useServerEffect=(requestId,handler,options={})=>{const{hydrate=true,skip=false}=options;const interceptedHandler=useRequestInterception(requestId,handler);const cachedResult=SsrCache.Default.getEntry(requestId);const maybeTrack=useContext(TrackerContext);if(!skip&&cachedResult==null&&Server.isServerSide()){maybeTrack?.(requestId,interceptedHandler,hydrate);}return cachedResult==null?null:resultFromCachedResponse(cachedResult)};
40
40
 
41
- const DefaultScope$1="useCachedEffect";const useCachedEffect=(requestId,handler,options={})=>{const{fetchPolicy=FetchPolicy.CacheBeforeNetwork,skip:hardSkip=false,retainResultOnChange=false,onResultChanged,scope=DefaultScope$1}=options;const interceptedHandler=useRequestInterception(requestId,handler);const[mostRecentResult,setMostRecentResult]=useSharedCache(requestId,scope);const forceUpdate=useForceUpdate();const networkResultRef=React.useRef();const currentRequestRef=React.useRef();const fetchRequest=React.useMemo(()=>{currentRequestRef.current?.cancel();currentRequestRef.current=null;networkResultRef.current=null;const fetchFn=()=>{if(fetchPolicy===FetchPolicy.CacheOnly){throw new DataError("Cannot fetch with CacheOnly policy",DataErrors.NotAllowed)}const request=RequestFulfillment.Default.fulfill(`${requestId}|${scope}`,{handler:interceptedHandler});if(request===currentRequestRef.current?.request){return}networkResultRef.current=null;currentRequestRef.current?.cancel();let cancel=false;request.then(result=>{currentRequestRef.current=null;if(cancel){return}setMostRecentResult(result);networkResultRef.current=result;if(onResultChanged!=null){onResultChanged(result);}else {forceUpdate();}return});currentRequestRef.current={requestId,request,cancel(){cancel=true;RequestFulfillment.Default.abort(requestId);}};};return fetchFn},[requestId,onResultChanged,forceUpdate,setMostRecentResult,fetchPolicy]);const shouldFetch=React.useMemo(()=>{if(hardSkip){return false}switch(fetchPolicy){case FetchPolicy.CacheOnly:return false;case FetchPolicy.CacheBeforeNetwork:return mostRecentResult==null;case FetchPolicy.CacheAndNetwork:case FetchPolicy.NetworkOnly:return networkResultRef.current==null}},[mostRecentResult,fetchPolicy,hardSkip]);React.useEffect(()=>{if(!shouldFetch){return}fetchRequest();return ()=>{currentRequestRef.current?.cancel();currentRequestRef.current=null;}},[shouldFetch,fetchRequest]);const lastResultAgnosticOfIdRef=React.useRef(Status.noData());const loadingResult=retainResultOnChange?lastResultAgnosticOfIdRef.current:shouldFetch?Status.loading():Status.noData();const result=(fetchPolicy===FetchPolicy.NetworkOnly?networkResultRef.current:mostRecentResult)??loadingResult;lastResultAgnosticOfIdRef.current=result;return [result,fetchRequest]};
41
+ const DefaultScope$1="useCachedEffect";const useCachedEffect=(requestId,handler,options={})=>{const{fetchPolicy=FetchPolicy.CacheBeforeNetwork,skip:hardSkip=false,retainResultOnChange=false,onResultChanged,scope=DefaultScope$1}=options;const interceptedHandler=useRequestInterception(requestId,handler);const[mostRecentResult,setMostRecentResult]=useSharedCache(requestId,scope);const forceUpdate=useForceUpdate();const onResultChangedRef=useLatestRef(onResultChanged);const networkResultRef=React.useRef();const currentRequestRef=React.useRef();const fetchRequest=React.useMemo(()=>{currentRequestRef.current?.cancel();currentRequestRef.current=null;networkResultRef.current=null;const fetchFn=()=>{if(fetchPolicy===FetchPolicy.CacheOnly){throw new DataError("Cannot fetch with CacheOnly policy",DataErrors.NotAllowed)}const request=RequestFulfillment.Default.fulfill(`${requestId}|${scope}`,{handler:interceptedHandler});if(request===currentRequestRef.current?.request){return}networkResultRef.current=null;currentRequestRef.current?.cancel();let cancel=false;request.then(result=>{currentRequestRef.current=null;if(cancel){return}setMostRecentResult(result);networkResultRef.current=result;const onResultChanged=onResultChangedRef.current;if(onResultChanged!=null){onResultChanged(result);}else {forceUpdate();}return});currentRequestRef.current={requestId,request,cancel(){cancel=true;RequestFulfillment.Default.abort(requestId);}};};return fetchFn},[requestId,forceUpdate,setMostRecentResult,fetchPolicy]);const shouldFetch=React.useMemo(()=>{if(hardSkip){return false}switch(fetchPolicy){case FetchPolicy.CacheOnly:return false;case FetchPolicy.CacheBeforeNetwork:return mostRecentResult==null;case FetchPolicy.CacheAndNetwork:case FetchPolicy.NetworkOnly:return networkResultRef.current==null}},[mostRecentResult,fetchPolicy,hardSkip]);React.useEffect(()=>{if(!shouldFetch){return}fetchRequest();return ()=>{currentRequestRef.current?.cancel();currentRequestRef.current=null;}},[shouldFetch,fetchRequest]);const lastResultAgnosticOfIdRef=React.useRef(Status.noData());const loadingResult=retainResultOnChange?lastResultAgnosticOfIdRef.current:shouldFetch?Status.loading():Status.noData();const result=(fetchPolicy===FetchPolicy.NetworkOnly?networkResultRef.current:mostRecentResult)??loadingResult;lastResultAgnosticOfIdRef.current=result;return [result,fetchRequest]};
42
42
 
43
43
  var WhenClientSide=/*#__PURE__*/function(WhenClientSide){WhenClientSide["DoNotHydrate"]="DoNotHydrate";WhenClientSide["ExecuteWhenNoResult"]="ExecuteWhenNoResult";WhenClientSide["ExecuteWhenNoSuccessResult"]="ExecuteWhenNoSuccessResult";WhenClientSide["AlwaysExecute"]="AlwaysExecute";return WhenClientSide}({});const DefaultScope="useHydratableEffect";const useHydratableEffect=(requestId,handler,options={})=>{const{clientBehavior="ExecuteWhenNoSuccessResult",skip=false,retainResultOnChange=false,onResultChanged,scope=DefaultScope}=options;const serverResult=useServerEffect(requestId,handler,{hydrate:clientBehavior!=="DoNotHydrate",skip});const getDefaultCacheValue=React.useCallback(()=>{switch(clientBehavior){case"DoNotHydrate":case"AlwaysExecute":return null;case"ExecuteWhenNoResult":return serverResult;case"ExecuteWhenNoSuccessResult":if(serverResult?.status==="success"){return serverResult}return null}},[serverResult]);useSharedCache(requestId,scope,getDefaultCacheValue);const[clientResult]=useCachedEffect(requestId,handler,{skip,onResultChanged,retainResultOnChange,scope,fetchPolicy:FetchPolicy.CacheBeforeNetwork});return serverResult??clientResult};
44
44
 
package/dist/index.js CHANGED
@@ -61,7 +61,7 @@ const useRequestInterception=(requestId,handler)=>{const interceptors=React__nam
61
61
 
62
62
  const useServerEffect=(requestId,handler,options={})=>{const{hydrate=true,skip=false}=options;const interceptedHandler=useRequestInterception(requestId,handler);const cachedResult=SsrCache.Default.getEntry(requestId);const maybeTrack=React.useContext(TrackerContext);if(!skip&&cachedResult==null&&wonderBlocksCore.Server.isServerSide()){maybeTrack?.(requestId,interceptedHandler,hydrate);}return cachedResult==null?null:resultFromCachedResponse(cachedResult)};
63
63
 
64
- const DefaultScope$1="useCachedEffect";const useCachedEffect=(requestId,handler,options={})=>{const{fetchPolicy=FetchPolicy.CacheBeforeNetwork,skip:hardSkip=false,retainResultOnChange=false,onResultChanged,scope=DefaultScope$1}=options;const interceptedHandler=useRequestInterception(requestId,handler);const[mostRecentResult,setMostRecentResult]=useSharedCache(requestId,scope);const forceUpdate=wonderBlocksCore.useForceUpdate();const networkResultRef=React__namespace.useRef();const currentRequestRef=React__namespace.useRef();const fetchRequest=React__namespace.useMemo(()=>{currentRequestRef.current?.cancel();currentRequestRef.current=null;networkResultRef.current=null;const fetchFn=()=>{if(fetchPolicy===FetchPolicy.CacheOnly){throw new DataError("Cannot fetch with CacheOnly policy",DataErrors.NotAllowed)}const request=RequestFulfillment.Default.fulfill(`${requestId}|${scope}`,{handler:interceptedHandler});if(request===currentRequestRef.current?.request){return}networkResultRef.current=null;currentRequestRef.current?.cancel();let cancel=false;request.then(result=>{currentRequestRef.current=null;if(cancel){return}setMostRecentResult(result);networkResultRef.current=result;if(onResultChanged!=null){onResultChanged(result);}else {forceUpdate();}return});currentRequestRef.current={requestId,request,cancel(){cancel=true;RequestFulfillment.Default.abort(requestId);}};};return fetchFn},[requestId,onResultChanged,forceUpdate,setMostRecentResult,fetchPolicy]);const shouldFetch=React__namespace.useMemo(()=>{if(hardSkip){return false}switch(fetchPolicy){case FetchPolicy.CacheOnly:return false;case FetchPolicy.CacheBeforeNetwork:return mostRecentResult==null;case FetchPolicy.CacheAndNetwork:case FetchPolicy.NetworkOnly:return networkResultRef.current==null}},[mostRecentResult,fetchPolicy,hardSkip]);React__namespace.useEffect(()=>{if(!shouldFetch){return}fetchRequest();return ()=>{currentRequestRef.current?.cancel();currentRequestRef.current=null;}},[shouldFetch,fetchRequest]);const lastResultAgnosticOfIdRef=React__namespace.useRef(Status.noData());const loadingResult=retainResultOnChange?lastResultAgnosticOfIdRef.current:shouldFetch?Status.loading():Status.noData();const result=(fetchPolicy===FetchPolicy.NetworkOnly?networkResultRef.current:mostRecentResult)??loadingResult;lastResultAgnosticOfIdRef.current=result;return [result,fetchRequest]};
64
+ const DefaultScope$1="useCachedEffect";const useCachedEffect=(requestId,handler,options={})=>{const{fetchPolicy=FetchPolicy.CacheBeforeNetwork,skip:hardSkip=false,retainResultOnChange=false,onResultChanged,scope=DefaultScope$1}=options;const interceptedHandler=useRequestInterception(requestId,handler);const[mostRecentResult,setMostRecentResult]=useSharedCache(requestId,scope);const forceUpdate=wonderBlocksCore.useForceUpdate();const onResultChangedRef=wonderBlocksCore.useLatestRef(onResultChanged);const networkResultRef=React__namespace.useRef();const currentRequestRef=React__namespace.useRef();const fetchRequest=React__namespace.useMemo(()=>{currentRequestRef.current?.cancel();currentRequestRef.current=null;networkResultRef.current=null;const fetchFn=()=>{if(fetchPolicy===FetchPolicy.CacheOnly){throw new DataError("Cannot fetch with CacheOnly policy",DataErrors.NotAllowed)}const request=RequestFulfillment.Default.fulfill(`${requestId}|${scope}`,{handler:interceptedHandler});if(request===currentRequestRef.current?.request){return}networkResultRef.current=null;currentRequestRef.current?.cancel();let cancel=false;request.then(result=>{currentRequestRef.current=null;if(cancel){return}setMostRecentResult(result);networkResultRef.current=result;const onResultChanged=onResultChangedRef.current;if(onResultChanged!=null){onResultChanged(result);}else {forceUpdate();}return});currentRequestRef.current={requestId,request,cancel(){cancel=true;RequestFulfillment.Default.abort(requestId);}};};return fetchFn},[requestId,forceUpdate,setMostRecentResult,fetchPolicy]);const shouldFetch=React__namespace.useMemo(()=>{if(hardSkip){return false}switch(fetchPolicy){case FetchPolicy.CacheOnly:return false;case FetchPolicy.CacheBeforeNetwork:return mostRecentResult==null;case FetchPolicy.CacheAndNetwork:case FetchPolicy.NetworkOnly:return networkResultRef.current==null}},[mostRecentResult,fetchPolicy,hardSkip]);React__namespace.useEffect(()=>{if(!shouldFetch){return}fetchRequest();return ()=>{currentRequestRef.current?.cancel();currentRequestRef.current=null;}},[shouldFetch,fetchRequest]);const lastResultAgnosticOfIdRef=React__namespace.useRef(Status.noData());const loadingResult=retainResultOnChange?lastResultAgnosticOfIdRef.current:shouldFetch?Status.loading():Status.noData();const result=(fetchPolicy===FetchPolicy.NetworkOnly?networkResultRef.current:mostRecentResult)??loadingResult;lastResultAgnosticOfIdRef.current=result;return [result,fetchRequest]};
65
65
 
66
66
  var WhenClientSide=/*#__PURE__*/function(WhenClientSide){WhenClientSide["DoNotHydrate"]="DoNotHydrate";WhenClientSide["ExecuteWhenNoResult"]="ExecuteWhenNoResult";WhenClientSide["ExecuteWhenNoSuccessResult"]="ExecuteWhenNoSuccessResult";WhenClientSide["AlwaysExecute"]="AlwaysExecute";return WhenClientSide}({});const DefaultScope="useHydratableEffect";const useHydratableEffect=(requestId,handler,options={})=>{const{clientBehavior="ExecuteWhenNoSuccessResult",skip=false,retainResultOnChange=false,onResultChanged,scope=DefaultScope}=options;const serverResult=useServerEffect(requestId,handler,{hydrate:clientBehavior!=="DoNotHydrate",skip});const getDefaultCacheValue=React__namespace.useCallback(()=>{switch(clientBehavior){case"DoNotHydrate":case"AlwaysExecute":return null;case"ExecuteWhenNoResult":return serverResult;case"ExecuteWhenNoSuccessResult":if(serverResult?.status==="success"){return serverResult}return null}},[serverResult]);useSharedCache(requestId,scope,getDefaultCacheValue);const[clientResult]=useCachedEffect(requestId,handler,{skip,onResultChanged,retainResultOnChange,scope,fetchPolicy:FetchPolicy.CacheBeforeNetwork});return serverResult??clientResult};
67
67
 
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "",
4
4
  "author": "Khan Academy",
5
5
  "license": "MIT",
6
- "version": "15.0.2",
6
+ "version": "15.0.3",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -29,7 +29,7 @@
29
29
  "devDependencies": {
30
30
  "@khanacademy/wonder-stuff-testing": "^4.0.3",
31
31
  "@khanacademy/wb-dev-build-settings": "3.3.0",
32
- "@khanacademy/wonder-blocks-testing-core": "5.0.0"
32
+ "@khanacademy/wonder-blocks-testing-core": "5.1.1"
33
33
  },
34
34
  "scripts": {
35
35
  "test": "echo \"Error: no test specified\" && exit 1"