Skip to content

Commit 4b37d2d

Browse files
committed
Refactor deployment update handling in the React component. Replaced useRef with useState for initial deployment ID management, improving state handling and avoiding stale closures. Introduced useMemo to derive update availability based on deployment changes and user dismissals, enhancing performance and clarity.
1 parent 41675a6 commit 4b37d2d

2 files changed

Lines changed: 20 additions & 19 deletions

File tree

src/component/lib.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { v } from "convex/values";
22
import { mutation, query, internalMutation } from "./_generated/server.js";
3-
import schema from "./schema.js";
43

54
// Validator for static asset documents (including system fields)
65
const staticAssetValidator = v.object({

src/react/index.tsx

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client";
22

33
import { useQuery } from "convex/react";
4-
import { useEffect, useRef, useState } from "react";
4+
import { useState, useMemo } from "react";
55
import type { FunctionReference } from "convex/server";
66

77
type DeploymentInfo = {
@@ -46,34 +46,36 @@ export function useDeploymentUpdates(
4646
getCurrentDeployment: FunctionReference<"query", "public", Record<string, never>, DeploymentInfo>,
4747
) {
4848
const deployment = useQuery(getCurrentDeployment, {});
49-
const initialDeploymentId = useRef<string | null>(null);
50-
const [updateAvailable, setUpdateAvailable] = useState(false);
49+
const [initialDeploymentId, setInitialDeploymentId] = useState<string | null>(null);
50+
const [dismissedDeploymentId, setDismissedDeploymentId] = useState<string | null>(null);
5151

52-
useEffect(() => {
53-
if (!deployment) return;
52+
// Capture the initial deployment ID on first load
53+
// Using useState with functional update to avoid stale closure issues
54+
if (deployment && initialDeploymentId === null) {
55+
// This is safe - we're setting initial state based on first data load
56+
// It only runs once when deployment first becomes available
57+
setInitialDeploymentId(deployment.currentDeploymentId);
58+
}
5459

55-
// Store the initial deployment ID on first load
56-
if (initialDeploymentId.current === null) {
57-
initialDeploymentId.current = deployment.currentDeploymentId;
58-
return;
60+
// Derive updateAvailable from current state
61+
const updateAvailable = useMemo(() => {
62+
if (!deployment || initialDeploymentId === null) {
63+
return false;
5964
}
60-
61-
// Check if deployment changed
62-
if (deployment.currentDeploymentId !== initialDeploymentId.current) {
63-
setUpdateAvailable(true);
64-
}
65-
}, [deployment]);
65+
// Show update if deployment changed from initial AND user hasn't dismissed this one
66+
const hasNewDeployment = deployment.currentDeploymentId !== initialDeploymentId;
67+
const isDismissed = deployment.currentDeploymentId === dismissedDeploymentId;
68+
return hasNewDeployment && !isDismissed;
69+
}, [deployment, initialDeploymentId, dismissedDeploymentId]);
6670

6771
const reload = () => {
6872
window.location.reload();
6973
};
7074

7175
const dismiss = () => {
72-
// Update the stored deployment ID so we don't show the banner again
7376
if (deployment) {
74-
initialDeploymentId.current = deployment.currentDeploymentId;
77+
setDismissedDeploymentId(deployment.currentDeploymentId);
7578
}
76-
setUpdateAvailable(false);
7779
};
7880

7981
return {

0 commit comments

Comments
 (0)