Skip to content

Commit 7dc0352

Browse files
authored
perf(matching): memoize list components to prevent re-renders (#238)
1 parent 27cfa41 commit 7dc0352

2 files changed

Lines changed: 108 additions & 60 deletions

File tree

β€Žreplace_memo.pyβ€Ž

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import re
2+
3+
with open('src/pages/matching-page.tsx', 'r') as f:
4+
content = f.read()
5+
6+
# Replace TalentCard
7+
content = re.sub(
8+
r'function TalentCard\(\{ match, isLoggedIn \}: \{ match: MatchResult; isLoggedIn: boolean \}\) \{',
9+
r'const TalentCard = React.memo(function TalentCard({ match, isLoggedIn }: { match: MatchResult; isLoggedIn: boolean }) {',
10+
content
11+
)
12+
content = re.sub(
13+
r' </div>\n \);\n\}\n\n// ─── Tab: Projets',
14+
r' </div>\n );\n});\n\n// ─── Tab: Projets',
15+
content
16+
)
17+
18+
# Replace ProjectCard
19+
content = re.sub(
20+
r'function ProjectCard\(\{\n project,\n score,\n reasons,\n isLoggedIn,\n isInterested,\n onInterest,\n\}: \{\n project: Project;\n score\?: number;\n reasons\?: string\[\];\n isLoggedIn: boolean;\n isInterested: boolean;\n onInterest: \(\) => void;\n\}\) \{',
21+
r'const ProjectCard = React.memo(function ProjectCard({\n project,\n score,\n reasons,\n isLoggedIn,\n isInterested,\n onInterest,\n}: {\n project: Project;\n score?: number;\n reasons?: string[];\n isLoggedIn: boolean;\n isInterested: boolean;\n onInterest: () => void;\n}) {',
22+
content
23+
)
24+
25+
content = re.sub(
26+
r' </div>\n \);\n\}\n\n// ─── Tab: Connexions',
27+
r' </div>\n );\n});\n\n// ─── Tab: Connexions',
28+
content
29+
)
30+
31+
# Replace ConnectionRow
32+
content = re.sub(
33+
r'function ConnectionRow\(\{\n partner,\n conn,\n mode,\n\}: \{\n partner: \{ id: string; username: string; full_name: string; avatar_url: string \};\n userId: string;\n conn: ConnectionWithProfiles;\n mode: "incoming" \| "connected";\n\}\) \{',
34+
r'const ConnectionRow = React.memo(function ConnectionRow({\n partner,\n conn,\n mode,\n}: {\n partner: { id: string; username: string; full_name: string; avatar_url: string };\n userId: string;\n conn: ConnectionWithProfiles;\n mode: "incoming" | "connected";\n}) {',
35+
content
36+
)
37+
38+
content = re.sub(
39+
r' </div>\n \);\n\}\n$',
40+
r' </div>\n );\n});\n',
41+
content
42+
)
43+
44+
45+
with open('src/pages/matching-page.tsx', 'w') as f:
46+
f.write(content)

β€Žsrc/pages/matching-page.tsxβ€Ž

Lines changed: 62 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useMemo, useRef, useState, useCallback } from "react";
1+
import React, { useEffect, useMemo, useRef, useState, useCallback } from "react";
22
import { Link, useSearchParams } from "react-router-dom";
33
import {
44
Handshake,
@@ -329,7 +329,7 @@ function TalentsTab({
329329

330330
// ─── Talent Card ──────────────────────────────────────────────────────────────
331331

332-
function TalentCard({ match, isLoggedIn }: { match: MatchResult; isLoggedIn: boolean }) {
332+
const TalentCard = React.memo(function TalentCard({ match, isLoggedIn }: { match: MatchResult; isLoggedIn: boolean }) {
333333
const { profile, score, reasons } = match;
334334

335335
return (
@@ -414,7 +414,7 @@ function TalentCard({ match, isLoggedIn }: { match: MatchResult; isLoggedIn: boo
414414
</div>
415415
</div>
416416
);
417-
}
417+
});
418418

419419
// ─── Tab: Projets ─────────────────────────────────────────────────────────────
420420

@@ -454,6 +454,60 @@ function ProjetsTab({
454454
}
455455
}, [profile, user]);
456456

457+
const handleInterest = useCallback(async (projectId: string) => {
458+
if (!user) {
459+
toast.error("Veuillez vous connecter pour exprimer votre intΓ©rΓͺt.");
460+
return;
461+
}
462+
463+
const isCurrentlyInterested = interested.has(projectId);
464+
if (isCurrentlyInterested) {
465+
// Quitter le groupe de projet
466+
const { error } = await supabase
467+
.from("project_members")
468+
.delete()
469+
.eq("project_id", projectId)
470+
.eq("profile_id", user.id);
471+
472+
if (!error) {
473+
setInterested((prev) => {
474+
const next = new Set(prev);
475+
next.delete(projectId);
476+
return next;
477+
});
478+
toast.success("Vous n'Γͺtes plus inscrit Γ  ce projet.");
479+
} else {
480+
toast.error("Impossible de se dΓ©sinscrire.");
481+
}
482+
} else {
483+
// Rejoindre le groupe de projet comme collaborateur
484+
const { error } = await supabase
485+
.from("project_members")
486+
.insert({
487+
project_id: projectId,
488+
profile_id: user.id,
489+
role: "collaborator"
490+
});
491+
492+
if (!error) {
493+
setInterested((prev) => {
494+
const next = new Set(prev);
495+
next.add(projectId);
496+
return next;
497+
});
498+
toast.success("Vous avez rejoint le groupe de projet !", {
499+
description: "Retrouvez la discussion d'Γ©quipe sous l'onglet Groupes de Projets dans vos Messages.",
500+
});
501+
} else {
502+
if (error.code === "PGRST116" || error.message?.includes("relation")) {
503+
toast.error("La base de donnΓ©es n'a pas encore Γ©tΓ© configurΓ©e pour le chat de groupe.");
504+
} else {
505+
toast.error("Impossible de rejoindre le projet.");
506+
}
507+
}
508+
}
509+
}, [user, interested]);
510+
457511
const displayList: { project: Project; score?: number; reasons?: string[] }[] =
458512
profile && projectMatches.length > 0
459513
? projectMatches.map((m) => ({ project: m.project, score: m.score, reasons: m.reasons }))
@@ -489,59 +543,7 @@ function ProjetsTab({
489543
reasons={reasons}
490544
isLoggedIn={!!user}
491545
isInterested={interested.has(project.id)}
492-
onInterest={async () => {
493-
if (!user) {
494-
toast.error("Veuillez vous connecter pour exprimer votre intΓ©rΓͺt.");
495-
return;
496-
}
497-
498-
const isCurrentlyInterested = interested.has(project.id);
499-
if (isCurrentlyInterested) {
500-
// Quitter le groupe de projet
501-
const { error } = await supabase
502-
.from("project_members")
503-
.delete()
504-
.eq("project_id", project.id)
505-
.eq("profile_id", user.id);
506-
507-
if (!error) {
508-
setInterested((prev) => {
509-
const next = new Set(prev);
510-
next.delete(project.id);
511-
return next;
512-
});
513-
toast.success("Vous n'Γͺtes plus inscrit Γ  ce projet.");
514-
} else {
515-
toast.error("Impossible de se dΓ©sinscrire.");
516-
}
517-
} else {
518-
// Rejoindre le groupe de projet comme collaborateur
519-
const { error } = await supabase
520-
.from("project_members")
521-
.insert({
522-
project_id: project.id,
523-
profile_id: user.id,
524-
role: "collaborator"
525-
});
526-
527-
if (!error) {
528-
setInterested((prev) => {
529-
const next = new Set(prev);
530-
next.add(project.id);
531-
return next;
532-
});
533-
toast.success("Vous avez rejoint le groupe de projet !", {
534-
description: "Retrouvez la discussion d'Γ©quipe sous l'onglet Groupes de Projets dans vos Messages.",
535-
});
536-
} else {
537-
if (error.code === "PGRST116" || error.message?.includes("relation")) {
538-
toast.error("La base de donnΓ©es n'a pas encore Γ©tΓ© configurΓ©e pour le chat de groupe.");
539-
} else {
540-
toast.error("Impossible de rejoindre le projet.");
541-
}
542-
}
543-
}
544-
}}
546+
onInterest={() => handleInterest(project.id)}
545547
/>
546548
))}
547549
</div>
@@ -583,7 +585,7 @@ function ProjetsTab({
583585

584586
// ─── Project Card ─────────────────────────────────────────────────────────────
585587

586-
function ProjectCard({
588+
const ProjectCard = React.memo(function ProjectCard({
587589
project,
588590
score,
589591
reasons,
@@ -704,7 +706,7 @@ function ProjectCard({
704706
</div>
705707
</div>
706708
);
707-
}
709+
});
708710

709711
// ─── Tab: Connexions ──────────────────────────────────────────────────────────
710712

@@ -827,7 +829,7 @@ function ConnexionsTab({ user }: { user: User | null }) {
827829

828830
// ─── Connection Row ───────────────────────────────────────────────────────────
829831

830-
function ConnectionRow({
832+
const ConnectionRow = React.memo(function ConnectionRow({
831833
partner,
832834
conn,
833835
mode,
@@ -887,4 +889,4 @@ function ConnectionRow({
887889
</div>
888890
</div>
889891
);
890-
}
892+
});

0 commit comments

Comments
Β (0)