Skip to content

Commit 056c442

Browse files
nullcoderclaude
andcommitted
perf: optimize Turnstile callbacks to prevent re-renders
- Add useCallback hooks for all Turnstile event handlers in create page - Create stable callback references with empty dependency arrays - Move Turnstile component outside main form content - Fix TypeScript error by adding mockExecute to test suite - Run prettier formatting on all files - Ensures Turnstile widget only renders once, improving performance 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 7b967af commit 056c442

File tree

2 files changed

+35
-29
lines changed

2 files changed

+35
-29
lines changed

app/create/page.tsx

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,27 @@ export default function CreateGistPage() {
5959
const [turnstileToken, setTurnstileToken] = useState<string | null>(null);
6060
const [isTurnstileReady, setIsTurnstileReady] = useState(false);
6161

62+
// Stable callback references to prevent Turnstile re-renders
63+
const handleTurnstileSuccess = useCallback((token: string) => {
64+
setTurnstileToken(token);
65+
setIsTurnstileReady(true);
66+
}, []);
67+
68+
const handleTurnstileError = useCallback(() => {
69+
setError(
70+
"🛡️ Security check failed. Please refresh the page and try again."
71+
);
72+
setIsTurnstileReady(false);
73+
}, []);
74+
75+
const handleTurnstileExpire = useCallback(() => {
76+
setTurnstileToken(null);
77+
setIsTurnstileReady(false);
78+
setError(
79+
"⏰ Security verification expired. Please refresh the page to continue."
80+
);
81+
}, []);
82+
6283
const handleFilesChange = useCallback((newFiles: FileData[]) => {
6384
setFiles(newFiles);
6485
// Don't clear errors on file change - let them persist
@@ -321,35 +342,6 @@ export default function CreateGistPage() {
321342
</CardContent>
322343
</Card>
323344

324-
{/* Invisible Turnstile Verification */}
325-
{turnstileSiteKey && (
326-
<div className="hidden">
327-
<Turnstile
328-
sitekey={turnstileSiteKey}
329-
action="create_gist"
330-
onSuccess={(token) => {
331-
setTurnstileToken(token);
332-
setIsTurnstileReady(true);
333-
}}
334-
onError={() => {
335-
setError(
336-
"🛡️ Security check failed. Please refresh the page and try again."
337-
);
338-
setIsTurnstileReady(false);
339-
}}
340-
onExpire={() => {
341-
setTurnstileToken(null);
342-
setIsTurnstileReady(false);
343-
setError(
344-
"⏰ Security verification expired. Please refresh the page to continue."
345-
);
346-
}}
347-
theme="auto"
348-
appearance="interaction-only"
349-
/>
350-
</div>
351-
)}
352-
353345
{/* Error Display */}
354346
{(error || validationMessage) && (
355347
<Alert variant="destructive">
@@ -394,6 +386,18 @@ export default function CreateGistPage() {
394386
)}
395387
</div>
396388

389+
{/* Invisible Turnstile Verification */}
390+
{turnstileSiteKey && (
391+
<Turnstile
392+
sitekey={turnstileSiteKey}
393+
action="create_gist"
394+
onSuccess={handleTurnstileSuccess}
395+
onError={handleTurnstileError}
396+
onExpire={handleTurnstileExpire}
397+
appearance="interaction-only"
398+
/>
399+
)}
400+
397401
{/* Share Dialog */}
398402
{shareUrl && (
399403
<ShareDialog

components/ui/turnstile.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ describe("Turnstile", () => {
66
const mockRender = vi.fn().mockReturnValue("widget-123");
77
const mockReset = vi.fn();
88
const mockRemove = vi.fn();
9+
const mockExecute = vi.fn();
910
const mockOnSuccess = vi.fn();
1011
const mockOnError = vi.fn();
1112
const mockOnExpire = vi.fn();
@@ -16,6 +17,7 @@ describe("Turnstile", () => {
1617
render: mockRender,
1718
reset: mockReset,
1819
remove: mockRemove,
20+
execute: mockExecute,
1921
};
2022

2123
vi.clearAllMocks();

0 commit comments

Comments
 (0)