refactor(BattleTable): extract resultMap logic and simplify table building

- Move `initResultMap` and `buildMatchGroupTable` outside the component to reduce closure dependencies.
- Update `ClubEventList` to remove obsolete page key storage logic.
- Add `MATCH_RESULT_MAP_KEY` constant to `utils/constants.ts`.
- Refactor `useMemo` and `useCallback` hooks to rely on the extracted functions.
- Ensure consistent dependency arrays in callbacks for reliable re-renders.
This commit is contained in:
kyuuseiryuu 2026-03-16 23:46:22 +09:00
parent ce5c523efb
commit f29974c111
3 changed files with 49 additions and 54 deletions

View File

@ -6,6 +6,7 @@ import styled from "styled-components";
import { cloneDeep } from "lodash";
import { ClearOutlined, EditOutlined, ShareAltOutlined } from "@ant-design/icons";
import { useLocation } from "react-router";
import { MATCH_RESULT_MAP_KEY } from "../utils/constants";
const StyledContainer = styled(Flex)`
min-width: 300px;
@ -30,31 +31,16 @@ function getMatchKey(player1: BasePlayer, player2: BasePlayer) {
return `${player1.uid}-${player2.uid}`;
}
function BattleTable({
players: list,
}: {
players: BasePlayer[];
}) {
const playerList = useMemo(() => list.length % 2 === 0 ? list : [...list, {
uid: '',
score: '',
name: '-',
}], [list]);
const resultMap = useMemo(() => {
function initResultMap() {
const cache = localStorage.getItem('match-result-map');
const cacheEntries: [string, Result][] = cache ? Object.entries(JSON.parse(cache)) : [];
return new Map<string, {
winer: string,
score: number,
}>(cacheEntries);
}, []);
useEffect(() => {
return () => {
const cache = JSON.stringify(Object.fromEntries(resultMap.entries()));
localStorage.setItem('match-result-map', cache);
}
}, [resultMap]);
const buildMatchGroupTable = useCallback((list: (BasePlayer | null)[]) => {
}
const buildMatchGroupTable = (list: (BasePlayer | null)[], resultMap: Map<string, Result>) => {
const roundNum = list.length - 1;
const dataList = cloneDeep(list);
const roundTable = new Array(roundNum).fill(0).map((_, i) => {
@ -76,8 +62,22 @@ function BattleTable({
});
return cloneDeep([left, right]);
});
const cache = JSON.stringify(Object.fromEntries(resultMap.entries()));
localStorage.setItem(MATCH_RESULT_MAP_KEY, cache);
return roundTable;
}, []);
};
function BattleTable({
players: list,
}: {
players: BasePlayer[];
}) {
const playerList = useMemo(() => list.length % 2 === 0 ? list : [...list, {
uid: '',
score: '',
name: '-',
}], [list]);
const resultMap = useMemo(() => initResultMap(), []);
const [matchGroupTable, setMatchGroupTable] = useState<ReturnType<typeof buildMatchGroupTable>>([]);
const matchKeys = useMemo(() => {
const keys: string[] = [];
@ -99,16 +99,16 @@ function BattleTable({
} else {
resultMap.set(key, { winer: winer?.uid!, score });
}
setMatchGroupTable(buildMatchGroupTable(playerList));
}, []);
setMatchGroupTable(buildMatchGroupTable(playerList, resultMap));
}, [playerList, resultMap]);
const handleClear = useCallback((left?: (BasePlayer | null | undefined)[], right?: (BasePlayer | null | undefined)[]) => {
left?.forEach((l, i) => {
const r = right?.[i];
const key = getMatchKey(l!, r!);
resultMap.delete(key);
});
setMatchGroupTable(buildMatchGroupTable(playerList));
}, [matchGroupTable, playerList]);
setMatchGroupTable(buildMatchGroupTable(playerList, resultMap));
}, [playerList, resultMap]);
const handleClearAll = useCallback(() => {
matchGroupTable.forEach(([left, right]) => {
left?.forEach((l, i) => {
@ -117,8 +117,8 @@ function BattleTable({
resultMap.delete(key);
});
});
setMatchGroupTable(buildMatchGroupTable(playerList));
}, [matchGroupTable, playerList]);
setMatchGroupTable(buildMatchGroupTable(playerList, resultMap));
}, [matchGroupTable, playerList, resultMap]);
const scoreDiff = useMemo(() => {
const [left = [], right = []] = matchGroupTable?.[matchGroupTable.length - 1] ?? [];
const newScoreMap = Object.fromEntries([...left, ...right].map(e => [e?.uid, e?.score]));
@ -154,9 +154,9 @@ function BattleTable({
setImportModalVisible(false);
}, []);
useEffect(() => {
const roundTable = buildMatchGroupTable(playerList);
const roundTable = buildMatchGroupTable(playerList, resultMap);
setMatchGroupTable(roundTable);
}, [playerList]);
}, [playerList, resultMap]);
const [shareCode, setShareCode] = useState('');
const handleImportCode = useCallback(async () => {
const data: Record<string, Result> = await fetch(`/api/battle/${eventId}?code=${shareCode}`)
@ -166,7 +166,7 @@ function BattleTable({
const { winer = '', score = 0 } = v ?? {};
resultMap.set(k, { winer, score });
});
setMatchGroupTable(buildMatchGroupTable(playerList));
setMatchGroupTable(buildMatchGroupTable(playerList, resultMap));
setImportModalVisible(false);
}, [shareCode, playerList]);
// console.debug('matchGroupTable', matchGroupTable, gameResultMap);

View File

@ -4,7 +4,6 @@ import { useCallback, useEffect, useMemo, useState } from "react";
import { EventCard } from "./EventCard";
import { useRequest } from "ahooks";
import { CalendarOutlined } from "@ant-design/icons";
import { STORE_PAGE_LIST_KEY } from "../utils/constants";
interface Props {
clubId: string;
@ -44,11 +43,6 @@ export const ClubEvenList = (props: Props) => {
}, [showFinishedEvents, requestEvents.data?.data]);
const onPageInit = useCallback(async () => {
const storePageKey = getStorageKey(props.clubId);
const keys: string[] = JSON.parse(localStorage.getItem(STORE_PAGE_LIST_KEY) ?? '[]');
if (!keys.includes(storePageKey)) {
keys.push(storePageKey);
localStorage.setItem(STORE_PAGE_LIST_KEY, JSON.stringify(keys));
}
const prePage = Number(sessionStorage.getItem(storePageKey)) || 1;
const data = await requestEvents.runAsync(props.clubId, prePage).then(res => res.data);
setPage(prePage);

View File

@ -1,3 +1,4 @@
export const LOGTO_RESOURCE = 'https://tt.ksr.la';
export const CLUB_SELECTOR_KEY = 'CLUB_SELECTOR';
export const STORE_PAGE_LIST_KEY = 'events-page-keys';
export const MATCH_RESULT_MAP_KEY = 'match-result-map';