diff --git a/components/Avatar.tsx b/components/Avatar.tsx index f38ec43e..22b57242 100644 --- a/components/Avatar.tsx +++ b/components/Avatar.tsx @@ -33,6 +33,7 @@ export default function Avatar(props: { > {"Avatar"} diff --git a/components/Container.tsx b/components/Container.tsx index dd0a17d6..c04cd4d4 100644 --- a/components/Container.tsx +++ b/components/Container.tsx @@ -125,7 +125,7 @@ export default function Container(props: ContainerProps) { }; loadSelectedSeasons(); - }, [selectedTeamIndex, teams]); + }, [selectedTeamIndex, teams, selectedTeam]); let showAuthBlock = false; if (props.requireAuthentication) { diff --git a/components/Footer.tsx b/components/Footer.tsx index 209c63d9..6a93661d 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -58,6 +58,7 @@ export default function Footer() { 4026 Bench

Made with ❤️ by{" "} @@ -181,7 +182,7 @@ export default function Footer() { src="/statbotics.webp" width="15" height="25" - alt="tba" + alt="Statbotics Logo" className="mr-6" /> Statbotics @@ -195,7 +196,7 @@ export default function Footer() { src="/FIRST.png" width="90" height="25" - alt="tba" + alt="FIRST Logo" /> diff --git a/components/UpdateModal.tsx b/components/UpdateModal.tsx index ed9c6fa8..9769506e 100644 --- a/components/UpdateModal.tsx +++ b/components/UpdateModal.tsx @@ -46,6 +46,7 @@ function News() { Broken Robot

Whats New?

diff --git a/components/competition/PitScoutingCard.tsx b/components/competition/PitScoutingCard.tsx index 6a1c84f4..4c54423d 100644 --- a/components/competition/PitScoutingCard.tsx +++ b/components/competition/PitScoutingCard.tsx @@ -50,7 +50,7 @@ export default function PitScoutingCard(props: {
{report.submitted ? ( img import("react-p5").then((mod) => mod.default), { let bg: p5Types.Image; let dropped = false; -export default function FieldPositionSelector(props: { +export default function FieldPositionSelector({ + alliance, + fieldImagePrefix, + initialPos, + callback, +}: { alliance: AllianceColor; fieldImagePrefix: string; initialPos: FieldPos; callback: (key: string, value: FieldPos) => void; }) { - const [mx, setMx, getMx] = useDynamicState(props.initialPos?.x ?? 0); - const [my, setMy, getMy] = useDynamicState(props.initialPos?.y ?? 0); - const [a, setA, getA] = useDynamicState(props.initialPos?.angle ?? 0); + const [mx, setMx, getMx] = useDynamicState(initialPos?.x ?? 0); + const [my, setMy, getMy] = useDynamicState(initialPos?.y ?? 0); + const [a, setA, getA] = useDynamicState(initialPos?.angle ?? 0); const [ax, setAx] = useState(0); const [ay, setAy] = useState(0); useEffect(() => { - props.callback("AutoStart", { x: mx ?? 0, y: my ?? 0, angle: a ?? 0 }); - }, [mx, my, a, ax, ay]); + callback("AutoStart", { x: mx ?? 0, y: my ?? 0, angle: a ?? 0 }); + }, [mx, my, a, ax, ay, callback]); const setup = (p5: p5Types, canvasParentRef: Element) => { bg = p5.loadImage( - props.alliance === AllianceColor.Blue - ? `/fields/${props.fieldImagePrefix}Blue.png` - : `/fields/${props.fieldImagePrefix}Red.png`, + alliance === AllianceColor.Blue + ? `/fields/${fieldImagePrefix}Blue.png` + : `/fields/${fieldImagePrefix}Red.png`, ); const ctx = p5.createCanvas(350, 300).parent(canvasParentRef); diff --git a/components/forms/Form.tsx b/components/forms/Form.tsx index 612a634e..5ea60da8 100644 --- a/components/forms/Form.tsx +++ b/components/forms/Form.tsx @@ -64,11 +64,11 @@ export default function Form(props: FormProps) { ); } - const sync = async () => { + const sync = useCallback(async () => { setSyncing(true); await api.updateReport({ data: formData }, props.report?._id!); setSyncing(false); - }; + }, [formData, props.report?._id]); const setCallback = useCallback( (key: any, value: boolean | string | number | object) => { @@ -79,7 +79,7 @@ export default function Form(props: FormProps) { return copy; }); }, - [], + [sync], ); useCallback(() => { @@ -91,7 +91,7 @@ export default function Form(props: FormProps) { } setFormData(formData); - }, [props.report?.data]); + }, [formData, setCallback]); function elementToNode(element: FormElement) { const key = element.key as string; diff --git a/components/forms/ImageUpload.tsx b/components/forms/ImageUpload.tsx index 87c71e21..5bd66a98 100644 --- a/components/forms/ImageUpload.tsx +++ b/components/forms/ImageUpload.tsx @@ -67,6 +67,7 @@ export default function ImageUpload(props: { Uploaded robot image )} {uploadProgress > 0 ? ( diff --git a/components/stats/Picklist.tsx b/components/stats/Picklist.tsx index c9290801..b0eaff88 100644 --- a/components/stats/Picklist.tsx +++ b/components/stats/Picklist.tsx @@ -1,7 +1,7 @@ import { DbPicklist, Report } from "@/lib/Types"; import { useDrag, useDrop } from "react-dnd"; -import { ChangeEvent, useEffect, useState } from "react"; +import { ChangeEvent, useCallback, useEffect, useState } from "react"; import { FaArrowDown, FaArrowUp, FaPlus } from "react-icons/fa"; import ClientApi from "@/lib/api/ClientApi"; @@ -225,61 +225,69 @@ export default function PicklistScreen(props: { const teams = props.teams.map((team) => ({ number: team })); - function savePicklists(picklists: Picklist[]) { - const picklistDict = picklists.reduce( - (acc, picklist) => { - acc.picklists[picklist.name] = picklist.teams.map( - (team) => team.number, - ); - return acc; - }, - { - _id: props.picklist._id, - picklists: {}, - }, - ); - - api.updatePicklist(picklistDict); - } + const savePicklists = useCallback( + (picklists: Picklist[]) => { + const picklistDict = picklists.reduce( + (acc, picklist) => { + acc.picklists[picklist.name] = picklist.teams.map( + (team) => team.number, + ); + return acc; + }, + { + _id: props.picklist._id, + picklists: {}, + }, + ); + + api.updatePicklist(picklistDict); + }, + [props.picklist._id], + ); - function updatePicklist(picklist: Picklist) { - setPicklists((old) => { - const newPicklists = old.map((p) => { - if (p.index === picklist.index) { - return picklist; - } else { - return p; - } + const updatePicklist = useCallback( + (picklist: Picklist) => { + setPicklists((old) => { + const newPicklists = old.map((p) => { + if (p.index === picklist.index) { + return picklist; + } else { + return p; + } + }); + + savePicklists(newPicklists); + return newPicklists; }); + }, + [setPicklists, savePicklists], + ); - savePicklists(newPicklists); - return newPicklists; - }); - } - - function loadDbPicklist(picklistDict: DbPicklist) { - setPicklists( - Object.entries(picklistDict.picklists).map((picklist, index) => { - const newPicklist: Picklist = { - index, - name: picklist[0], - teams: picklist[1].map((team: number) => ({ number: team })), - update: updatePicklist, - }; - - for (const team of newPicklist.teams) { - team.picklistIndex = newPicklist.index; - } - - return newPicklist; - }), - ); - } + const loadDbPicklist = useCallback( + (picklistDict: DbPicklist) => { + setPicklists( + Object.entries(picklistDict.picklists).map((picklist, index) => { + const newPicklist: Picklist = { + index, + name: picklist[0], + teams: picklist[1].map((team: number) => ({ number: team })), + update: updatePicklist, + }; + + for (const team of newPicklist.teams) { + team.picklistIndex = newPicklist.index; + } + + return newPicklist; + }), + ); + }, + [updatePicklist], + ); useEffect(() => { if (loadingPicklists !== LoadState.NotLoaded) return; - console.log(props); setLoadingPicklists(LoadState.Loading); api.getPicklist(props.picklist?._id).then((picklist) => { if (picklist) { @@ -289,7 +297,14 @@ export default function PicklistScreen(props: { loadDbPicklist(props.picklist); setLoadingPicklists(LoadState.Loaded); - }); + }, [ + loadingPicklists, + LoadState.NotLoaded, + LoadState.Loading, + LoadState.Loaded, + props.picklist, + loadDbPicklist, + ]); const addPicklist = () => { const newPicklist: Picklist = { diff --git a/components/stats/SmallGraph.tsx b/components/stats/SmallGraph.tsx index da4bb0f6..5cf038ed 100644 --- a/components/stats/SmallGraph.tsx +++ b/components/stats/SmallGraph.tsx @@ -126,7 +126,7 @@ export default function SmallGraph(props: { ); }); } - }, [key]); + }, [key, currentTeam, datapoints, props.selectedReports, props.team]); if (!props.selectedReports) { return <>; diff --git a/components/stats/TeamPage.tsx b/components/stats/TeamPage.tsx index 247f08c8..df6114de 100644 --- a/components/stats/TeamPage.tsx +++ b/components/stats/TeamPage.tsx @@ -151,7 +151,26 @@ export default function TeamPage(props: { const game = games[props.gameId]; - const associateTeams = () => { + useEffect(() => { + const subjectiveReports: typeof teamSubjectiveReports = {}; + props.subjectiveReports.forEach((subjectiveReport) => { + for (const teamNumber of Object.keys(subjectiveReport.robotComments)) { + if (!Object.keys(subjectiveReports).includes(teamNumber)) { + subjectiveReports[Number(teamNumber)] = [subjectiveReport]; + } else { + subjectiveReports[Number(teamNumber)].push(subjectiveReport); + } + } + }); + setTeamSubjectiveReports(subjectiveReports); + }, [props.subjectiveReports]); + + const pointTotals = reports.map((report) => game.getAvgPoints([report])); + const avgPoints = game.getAvgPoints(reports); + const stDev = StandardDeviation(pointTotals); + + useEffect(() => { + console.log("Associating teams..."); const newTeamReports: typeof teamReports = {}; reports.forEach((report) => { if (!(report.robotNumber in newTeamReports)) { @@ -179,29 +198,6 @@ export default function TeamPage(props: { } }); setTeamSubjectiveReports(subjectiveReports); - }; - - useEffect(() => { - const subjectiveReports: typeof teamSubjectiveReports = {}; - props.subjectiveReports.forEach((subjectiveReport) => { - for (const teamNumber of Object.keys(subjectiveReport.robotComments)) { - if (!Object.keys(subjectiveReports).includes(teamNumber)) { - subjectiveReports[Number(teamNumber)] = [subjectiveReport]; - } else { - subjectiveReports[Number(teamNumber)].push(subjectiveReport); - } - } - }); - setTeamSubjectiveReports(subjectiveReports); - }, [props.subjectiveReports]); - - const pointTotals = reports.map((report) => game.getAvgPoints([report])); - const avgPoints = game.getAvgPoints(reports); - const stDev = StandardDeviation(pointTotals); - - useEffect(() => { - console.log("Associating teams..."); - associateTeams(); }, [reports, props.pitReports, props.subjectiveReports]); // Associate pit reports diff --git a/components/stats/TeamStats.tsx b/components/stats/TeamStats.tsx index 75ebf0a7..89c950f8 100644 --- a/components/stats/TeamStats.tsx +++ b/components/stats/TeamStats.tsx @@ -33,6 +33,13 @@ export default function TeamStats(props: { { matchNum: number; content: { order: number; jsx: ReactNode }[] }[] | null >(null); + const pitReport = props.pitReport; + const badges = props.getBadges( + pitReport ?? undefined, + props.selectedReports, + false, + ); + useEffect(() => { if (!props.selectedTeam) return; setComments(null); @@ -132,6 +139,7 @@ export default function TeamStats(props: { props.selectedReports, props.subjectiveReports, props.pitReport, + pitReport, ]); if (!props.selectedTeam) { @@ -144,13 +152,6 @@ export default function TeamStats(props: { ); } - const pitReport = props.pitReport; - const badges = props.getBadges( - pitReport ?? undefined, - props.selectedReports, - false, - ); - function getSections( header: string, stats: ( diff --git a/eslint.config.mjs b/eslint.config.mjs index 5ae82872..8534996d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -11,9 +11,11 @@ const compat = new FlatCompat({ allConfig: js.configs.all }); -export default [...compat.extends("next/core-web-vitals"), { +const config = [...compat.extends("next/core-web-vitals"), { rules: { "@next/next/no-img-element": "off", "@next/next/no-html-link-for-pages": "off", - } -}]; \ No newline at end of file + }, +}]; + +export default config; \ No newline at end of file diff --git a/lib/client/useDynamicState.ts b/lib/client/useDynamicState.ts index e6a021d9..5e4551ab 100644 --- a/lib/client/useDynamicState.ts +++ b/lib/client/useDynamicState.ts @@ -9,7 +9,7 @@ import { Dispatch, SetStateAction, useState } from "react"; * a parameter. * @todo Rework this! The forced undefined return type and the forced callback makes me want to barf. */ -export default function ( +function useDefaultState( initialState?: T, ): [ T | undefined, @@ -29,3 +29,5 @@ export default function ( }, ]; } + +export default useDefaultState; diff --git a/lib/client/useInterval.ts b/lib/client/useInterval.ts index e04c348a..dc3f445b 100644 --- a/lib/client/useInterval.ts +++ b/lib/client/useInterval.ts @@ -13,7 +13,7 @@ export default function useInterval( useEffect(() => { setId(setInterval(func, interval)); return () => clearInterval(id); - }, [func.name, interval, ...deps]); + }, [func.name, interval, func, id, deps]); return id; } diff --git a/lib/client/useLocalStorage.ts b/lib/client/useLocalStorage.ts index 1e5f2727..241e2d5c 100644 --- a/lib/client/useLocalStorage.ts +++ b/lib/client/useLocalStorage.ts @@ -12,7 +12,7 @@ export default function useLocalStorage(key: string) { if (localStorage && key.length > 0) { JSON.stringify(localStorage.getItem(key)); } - }, []); + }, [key]); return [data as Type, setCallback] as const; } diff --git a/package-lock.json b/package-lock.json index c76ff79c..b9d7728e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2996,9 +2996,10 @@ } }, "node_modules/@next/env": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.0.tgz", - "integrity": "sha512-UcCO481cROsqJuszPPXJnb7GGuLq617ve4xuAyyNG4VSSocJNtMU5Fsx+Lp6mlN8c7W58aZLc5y6D/2xNmaK+w==" + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.0.3.tgz", + "integrity": "sha512-t9Xy32pjNOvVn2AS+Utt6VmyrshbpfUMhIjFO60gI58deSo/KgLOp31XZ4O+kY/Is8WAGYwA5gR7kOb1eORDBA==", + "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { "version": "15.0.3", diff --git a/package.json b/package.json index 174e4e34..31b2628a 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "docker-build": "docker build -t gearbox .", "docker-start": "docker run -i -t -p 443:443 gearbox", "docker-prune": "docker container prune", - "lint": "eslint . --max-warnings=0", + "lint": "eslint . --max-warnings=0 --ignore-pattern \"coverage/**/*\"", "prettier-fix": "prettier --write ./**/*.{js,tsx,jsx,json,ts}", "prettier-check": "prettier --check ./**/*.{js,tsx,jsx,json,ts}", "test": "jest" diff --git a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/[reportId]/index.tsx b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/[reportId]/index.tsx index 12eeb60c..e1672a39 100644 --- a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/[reportId]/index.tsx +++ b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/[reportId]/index.tsx @@ -20,7 +20,7 @@ export default function Homepage(props: FormProps) { useEffect(() => { if (props.report) setInterval(() => api.checkInForReport(props.report._id!), 5000); - }, []); + }, [props.report]); return ( { + const regeneratePitReports = useCallback(async () => { console.log("Regenerating pit reports..."); api .regeneratePitReports(comp?._id!) @@ -129,7 +129,7 @@ export default function CompetitionIndex({ setLoadingPitreports(false); }); }); - }; + }, [comp?._id]); useEffect(() => { if (!reports) return; @@ -146,83 +146,90 @@ export default function CompetitionIndex({ setMatchesAssigned(matchesAssigned); }, [reports]); - const loadMatches = async (silent: boolean = false) => { - if (!silent) setLoadingMatches(true); + const loadMatches = useCallback( + async (silent: boolean = false) => { + if (!silent) setLoadingMatches(true); - window.location.hash = ""; - let matches: Match[] = await api.allCompetitionMatches(comp?._id!); + window.location.hash = ""; + let matches: Match[] = await api.allCompetitionMatches(comp?._id!); - if (!matches || matches.length === 0) { - setNoMatches(true); + if (!matches || matches.length === 0) { + setNoMatches(true); - if (!silent) setLoadingMatches(false); + if (!silent) setLoadingMatches(false); - return; - } + return; + } - matches?.sort((a, b) => a.number - b.number); + matches?.sort((a, b) => a.number - b.number); - setMatches(matches); + setMatches(matches); - api - .getSubjectiveReportsFromMatches(comp?._id ?? "", matches) - .then((reports) => { - setSubjectiveReports(reports); - - const newReportIds: { [key: string]: SubjectiveReport } = {}; - reports.forEach((report) => { - if (!report._id) { - return; - } - newReportIds[report._id] = report; + api + .getSubjectiveReportsFromMatches(comp?._id ?? "", matches) + .then((reports) => { + setSubjectiveReports(reports); + + const newReportIds: { [key: string]: SubjectiveReport } = {}; + reports.forEach((report) => { + if (!report._id) { + return; + } + newReportIds[report._id] = report; + }); + setSubjectiveReportsById(newReportIds); }); - setSubjectiveReportsById(newReportIds); - }); - if (!silent) setLoadingMatches(false); - }; + if (!silent) setLoadingMatches(false); + }, + [comp?._id], + ); - const loadReports = async (silent: boolean = false) => { - const scoutingStats = (reps: Report[]) => { - if (!silent) setLoadingScoutStats(true); - let submittedCount = 0; - reps.forEach((report) => { - if (report.submitted) { - submittedCount++; - } - }); + const loadReports = useCallback( + async (silent: boolean = false) => { + const scoutingStats = (reps: Report[]) => { + if (!silent) setLoadingScoutStats(true); + let submittedCount = 0; + reps.forEach((report) => { + if (report.submitted) { + submittedCount++; + } + }); - setSubmittedReports(submittedCount); - if (!silent) setLoadingScoutStats(false); - }; + setSubmittedReports(submittedCount); + if (!silent) setLoadingScoutStats(false); + }; - if (!silent) setLoadingReports(true); + if (!silent) setLoadingReports(true); - let newReports: Report[] = await api.competitionReports( - comp?._id!, - false, - false, - ); + let newReports: Report[] = await api.competitionReports( + comp?._id!, + false, + false, + ); - setReports(newReports); + setReports(newReports); - const newReportsById: { [key: string]: Report } = {}; - newReports?.forEach((report) => { - if (!report._id) { - return; - } - newReportsById[report._id] = report; - }); - setReportsById(newReportsById); + const newReportsById: { [key: string]: Report } = {}; + newReports?.forEach((report) => { + if (!report._id) { + return; + } + newReportsById[report._id] = report; + }); - if (!silent) setLoadingReports(false); + setReportsById(newReportsById); - scoutingStats(newReports); - }; + if (!silent) setLoadingReports(false); + + scoutingStats(newReports); + }, + [comp?._id], + ); useEffect(() => { setInterval(() => loadReports(true), 5000); - }, []); + }, [loadReports]); useEffect(() => { const loadUsers = async (silent: boolean = false) => { @@ -304,7 +311,20 @@ export default function CompetitionIndex({ if (!attemptedRegeneratingPitReports && comp?.pitReports.length === 0) { regeneratePitReports(); } - }, [assigningMatches]); + }, [ + assigningMatches, + attemptedRegeneratingPitReports, + comp?._id, + comp?.pitReports, + loadMatches, + loadReports, + matches, + pitreports, + regeneratePitReports, + reports, + team, + usersById, + ]); const assignScouters = async () => { setAssigningMatches(true); diff --git a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pitstats.tsx b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pitstats.tsx index a87d97a5..9ccd991b 100644 --- a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pitstats.tsx +++ b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/pitstats.tsx @@ -13,7 +13,7 @@ import { GetServerSideProps } from "next"; import { BsGearFill } from "react-icons/bs"; import ClientApi from "@/lib/api/ClientApi"; -import { useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { getDatabase } from "@/lib/MongoDB"; import { MostCommonValue, @@ -190,6 +190,7 @@ function TeamSlide(props: { {pit.teamNumber.toString()} ) : ( <> @@ -220,7 +221,7 @@ export default function Pitstats(props: { competition: Competition }) { const layout = games[comp.gameId].pitStatsLayout; - const loadReports = async () => { + const loadReports = useCallback(async () => { const newReports = (await api.competitionReports( comp._id!, true, @@ -326,18 +327,25 @@ export default function Pitstats(props: { competition: Competition }) { ); }); setSlides(newSlides); - }; + }, [ + comp._id, + comp.gameId, + comp.pitReports, + comp.tbaId, + layout, + usePublicData, + ]); useEffect(() => { loadReports(); - }, [usePublicData]); + }, [usePublicData, loadReports]); useEffect(() => { const i = setInterval(() => { loadReports(); }, 60 * 1000); return () => clearInterval(i); - }, [usePublicData]); + }, [usePublicData, loadReports]); function changeSlide( nextSlide: (prev: number) => number, @@ -350,26 +358,32 @@ export default function Pitstats(props: { competition: Competition }) { }); } - function nextSlide(automatic: boolean = false) { - changeSlide( - (n) => (n < slidesRef.current.length - 1 ? n + 1 : -1), - automatic, - ); - } + const nextSlide = useCallback( + (automatic: boolean = false) => { + changeSlide( + (n) => (n < slidesRef.current.length - 1 ? n + 1 : -1), + automatic, + ); + }, + [slidesRef], + ); - function prevSlide(automatic: boolean = false) { - changeSlide( - (n) => (n >= 0 ? n - 1 : slidesRef.current.length - 1), - automatic, - ); - } + const prevSlide = useCallback( + async (automatic: boolean = false) => { + changeSlide( + (n) => (n >= 0 ? n - 1 : slidesRef.current.length - 1), + automatic, + ); + }, + [slidesRef], + ); useEffect(() => { if (slides.length > 0 && cycleSlidesAutomatically) { const timer = setInterval(() => nextSlide(true), 5000); return () => clearInterval(timer); } - }, [slides, cycleSlidesAutomatically]); + }, [slides, cycleSlidesAutomatically, nextSlide]); useEffect(() => { if (!addedKeyListeners) { @@ -382,13 +396,13 @@ export default function Pitstats(props: { competition: Competition }) { }); setAddedKeyListeners(true); } - }); + }, [addedKeyListeners, nextSlide, prevSlide]); useEffect(() => { const msg = "Would you like to include public data? (Ok = Yes, Cancel = No)"; setUsePublicData(comp.tbaId !== NotLinkedToTba && confirm(msg)); - }, []); + }, [comp.tbaId]); function OverallSlide() { const graphs = layout.overallSlideStats.map((stat) => { diff --git a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/scouters.tsx b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/scouters.tsx index c612c617..78ab9dc0 100644 --- a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/scouters.tsx +++ b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/scouters.tsx @@ -230,7 +230,7 @@ export default function Scouters(props: { setShouldRegenerateScouterData(true); setLoading(false); }); - }); + }, [scouters, matches, reports, loading, comp?._id]); useEffect(() => { if (shouldRegenerateScouterData && scouters && matches && reports) { @@ -283,7 +283,13 @@ export default function Scouters(props: { setScouters(scouterDict); setShouldRegenerateScouterData(false); } - }, [shouldRegenerateScouterData, lastCountedMatch]); + }, [ + shouldRegenerateScouterData, + lastCountedMatch, + matches, + reports, + scouters, + ]); return ( { + const resync = useCallback(async () => { console.log("Resyncing..."); setUpdating(true); @@ -73,11 +73,11 @@ export default function Stats(props: { setUpdate(Date.now()); setUpdating(false); - }; + }, [pitReports.length, props.competition._id, usePublicData]); useEffect(() => { resync(); - }, [usePublicData]); + }, [usePublicData, resync]); const teams: Set = new Set(); reports.forEach((r) => teams.add(r.robotNumber)); diff --git a/pages/[teamSlug]/[seasonSlug]/createComp.tsx b/pages/[teamSlug]/[seasonSlug]/createComp.tsx index 21f3c2d6..474414c8 100644 --- a/pages/[teamSlug]/[seasonSlug]/createComp.tsx +++ b/pages/[teamSlug]/[seasonSlug]/createComp.tsx @@ -11,6 +11,7 @@ import { NotLinkedToTba } from "@/lib/client/ClientUtils"; import { defaultGameId } from "@/lib/client/GameId"; import { Analytics } from "@/lib/client/Analytics"; import { useCurrentSession } from "@/lib/client/useCurrentSession"; +import { useCallback } from "react"; const api = new ClientApi(); @@ -29,7 +30,7 @@ export default function CreateComp(props: ResolvedUrlData) { const [creatingComp, setCreatingComp] = useState(false); const [usePublicData, setUsePublicData] = useState(true); - const searchComp = async () => { + const searchComp = useCallback(async () => { if (!name) { return; } @@ -43,7 +44,7 @@ export default function CreateComp(props: ResolvedUrlData) { } setLoading(false); - }; + }, [name]); const createComp = async () => { setCreatingComp(true); @@ -79,7 +80,7 @@ export default function CreateComp(props: ResolvedUrlData) { useEffect(() => { searchComp(); - }, [name]); + }, [name, searchComp]); return (