fix: ESLint react-hooks 최신 규칙 호환
All checks were successful
Client CI/CD / test (push) Successful in 12s
Client CI/CD / deploy (push) Successful in 38s

- useCallback → 일반 함수 + eslint-disable (초기 데이터 fetch)
- ToastProvider: useCallback 프로퍼티 할당 → useMemo 객체 패턴
- SSAFYCallbackPage: eslint-disable-line 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-15 23:45:10 +09:00
parent 790e6e4c7f
commit 1a3be5f76b
5 changed files with 23 additions and 25 deletions

View File

@@ -1,4 +1,4 @@
import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAuth } from '../context/useAuth';
import { getDownloadInfo } from '../api/download';
@@ -14,15 +14,16 @@ export default function DownloadSection() {
const { user } = useAuth();
const navigate = useNavigate();
const loadInfo = useCallback(() => {
const loadInfo = () => {
setReady(false);
setLoadError(false);
getDownloadInfo()
.then((data) => { setInfo(data); setReady(true); })
.catch(() => { setLoadError(true); setReady(true); });
}, []);
};
useEffect(() => { loadInfo(); }, [loadInfo]);
// eslint-disable-next-line react-hooks/set-state-in-effect -- initial data fetch on mount
useEffect(() => { loadInfo(); }, []);
const handlePlay = async () => {
if (!user) {

View File

@@ -1,5 +1,5 @@
// TODO: Add tests for CRUD operations (load download info, upload launcher, upload game)
import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect } from 'react';
import { getDownloadInfo } from '../../api/download';
import UploadForm from './UploadForm';
import './AdminCommon.css';
@@ -9,7 +9,7 @@ export default function DownloadAdmin() {
const [loading, setLoading] = useState(true);
const [loadError, setLoadError] = useState('');
const load = useCallback(() => {
const load = () => {
setLoading(true);
setLoadError('');
getDownloadInfo()
@@ -22,9 +22,10 @@ export default function DownloadAdmin() {
setLoadError('배포 정보를 불러올 수 없습니다.');
})
.finally(() => setLoading(false));
}, []);
};
useEffect(() => { load(); }, [load]);
// eslint-disable-next-line react-hooks/set-state-in-effect -- initial data fetch on mount
useEffect(() => { load(); }, []);
if (loading) {
return (

View File

@@ -1,5 +1,5 @@
// TODO: Add tests for CRUD operations (list users, update role, delete user)
import { useState, useEffect, useCallback } from 'react';
import { useState, useEffect } from 'react';
import { getUsers, updateUserRole, deleteUser } from '../../api/users';
import { useAuth } from '../../context/useAuth';
import { useToast } from '../toast/useToast';
@@ -12,7 +12,7 @@ export default function UserAdmin() {
const { user: me } = useAuth();
const toast = useToast();
const load = useCallback(() => {
const load = () => {
setLoading(true);
setFetchError(false);
getUsers()
@@ -22,8 +22,9 @@ export default function UserAdmin() {
setFetchError(true);
})
.finally(() => setLoading(false));
}, []);
useEffect(() => { load(); }, [load]);
};
// eslint-disable-next-line react-hooks/set-state-in-effect -- initial data fetch on mount
useEffect(() => { load(); }, []);
const handleRoleToggle = async (u) => {
const newRole = u.role === 'admin' ? 'user' : 'admin';

View File

@@ -1,4 +1,4 @@
import { useState, useCallback, useRef } from 'react';
import { useState, useCallback, useMemo, useRef } from 'react';
import { ToastContext } from './toastContextValue';
import './Toast.css';
@@ -20,17 +20,12 @@ export function ToastProvider({ children }) {
return id;
}, [removeToast]);
// Attaching methods to the toast function works because useCallback keeps
// the reference stable across renders, so the .success/.error/.warn properties
// persist. This is a pragmatic pattern — a plain object would be cleaner but
// would change the public API consumed by useToast() callers.
// IMPORTANT: This pattern relies on `addToast` being stable (no dependencies that change).
// If addToast gains new dependencies, toast.success/error/warn will be stale on the old ref.
// In that case, refactor to return an object { info, success, error, warn } from context.
const toast = useCallback((message) => addToast(message, 'info'), [addToast]);
toast.success = useCallback((message) => addToast(message, 'success'), [addToast]);
toast.error = useCallback((message) => addToast(message, 'error'), [addToast]);
toast.warn = useCallback((message) => addToast(message, 'warn'), [addToast]);
const toast = useMemo(() => ({
info: (message) => addToast(message, 'info'),
success: (message) => addToast(message, 'success'),
error: (message) => addToast(message, 'error'),
warn: (message) => addToast(message, 'warn'),
}), [addToast]);
return (
<ToastContext.Provider value={toast}>

View File

@@ -20,7 +20,7 @@ export default function SSAFYCallbackPage() {
const code = searchParams.get('code');
if (!code) {
setError('인가 코드가 없습니다.');
setError('인가 코드가 없습니다.'); // eslint-disable-line react-hooks/set-state-in-effect -- error state from URL param check
return;
}