Skip to content

Commit b1741b7

Browse files
fix: use ref for refresh state checks to prevent race conditions
The previous commit switched to using isPullRefreshing state in event handlers, which caused stale closures and race conditions. Event handlers would see outdated state values, allowing multiple refreshes to start simultaneously. This fixes the issue by: - Re-adding isRefreshingRef for synchronous state checks - Setting the ref immediately in handleRefresh() before state updates - Updating all handlers to check isRefreshingRef.current instead of state - Keeping the 5-second cooldown as an additional safeguard The ref updates synchronously, blocking rapid-fire events instantly rather than waiting for React's state update cycle. Co-authored-by: Anthony <AnthonyRonning@users.noreply.github.com>
1 parent 5958ef5 commit b1741b7

1 file changed

Lines changed: 8 additions & 5 deletions

File tree

‎frontend/src/components/ChatHistoryList.tsx‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export function ChatHistoryList({
9191
constpullDistanceRef=useRef(0);
9292
constwheelDeltaAccumulator=useRef(0);
9393
constlastRefreshTime=useRef(0);
94+
constisRefreshingRef=useRef(false);
9495

9596
// Fetch initial conversations from API using the OpenSecret SDK
9697
const{ isPending, error }=useQuery({
@@ -185,6 +186,7 @@ export function ChatHistoryList({
185186

186187
// Pull-to-refresh handler
187188
consthandleRefresh=useCallback(async()=>{
189+
isRefreshingRef.current=true;
188190
setIsPullRefreshing(true);
189191
try{
190192
awaitpollForUpdates();
@@ -195,6 +197,7 @@ export function ChatHistoryList({
195197
setTimeout(()=>{
196198
setIsPullRefreshing(false);
197199
setPullDistance(0);
200+
isRefreshingRef.current=false;
198201
// Record the time when refresh completes for cooldown
199202
lastRefreshTime.current=Date.now();
200203
},300);
@@ -211,14 +214,14 @@ export function ChatHistoryList({
211214

212215
consthandleTouchStart=(e: TouchEvent)=>{
213216
// Only start pull if we're at the top of the scroll
214-
if(container.scrollTop===0&&!isPullRefreshing){
217+
if(container.scrollTop===0&&!isRefreshingRef.current){
215218
pullStartY.current=e.touches[0].clientY;
216219
isPulling.current=true;
217220
}
218221
};
219222

220223
consthandleTouchMove=(e: TouchEvent)=>{
221-
if(!isPulling.current||isPullRefreshing)return;
224+
if(!isPulling.current||isRefreshingRef.current)return;
222225

223226
constcurrentY=e.touches[0].clientY;
224227
constdistance=currentY-pullStartY.current;
@@ -258,14 +261,14 @@ export function ChatHistoryList({
258261
if(isDesktopPlatform)return;
259262

260263
// Only start pull if we're at the top of the scroll
261-
if(container.scrollTop===0&&!isPullRefreshing){
264+
if(container.scrollTop===0&&!isRefreshingRef.current){
262265
pullStartY.current=e.clientY;
263266
isPulling.current=true;
264267
}
265268
};
266269

267270
consthandleMouseMove=(e: MouseEvent)=>{
268-
if(!isPulling.current||isPullRefreshing||isDesktopPlatform)return;
271+
if(!isPulling.current||isRefreshingRef.current||isDesktopPlatform)return;
269272

270273
constcurrentY=e.clientY;
271274
constdistance=currentY-pullStartY.current;
@@ -302,7 +305,7 @@ export function ChatHistoryList({
302305
constWHEEL_THRESHOLD=-50;// Require accumulated scroll before triggering
303306

304307
consthandleWheel=(e: WheelEvent)=>{
305-
if(!isDesktopPlatform||isPullRefreshing)return;
308+
if(!isDesktopPlatform||isRefreshingRef.current)return;
306309

307310
// Only handle if the event target is within our container
308311
if(!container.contains(e.targetasNode))return;

0 commit comments

Comments
 (0)