fix: Resolve infinite render loop by memoizing context functions and values
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect, createContext, useContext } from "react"
|
import { useState, useEffect, createContext, useContext, useMemo, useCallback } from "react"
|
||||||
import { Routes, Route } from "react-router-dom"
|
import { Routes, Route } from "react-router-dom"
|
||||||
import { MainLayout } from "./components/layout/MainLayout"
|
import { MainLayout } from "./components/layout/MainLayout"
|
||||||
import { LoginForm } from "./components/auth/LoginForm"
|
import { LoginForm } from "./components/auth/LoginForm"
|
||||||
@@ -25,8 +25,15 @@ function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
const [isAuthenticated, setIsAuthenticated] = useState(false)
|
const [isAuthenticated, setIsAuthenticated] = useState(false)
|
||||||
const [user, setUser] = useState<UserResponse | null>(null)
|
const [user, setUser] = useState<UserResponse | null>(null)
|
||||||
|
|
||||||
|
const value = useMemo(() => ({
|
||||||
|
isAuthenticated,
|
||||||
|
setIsAuthenticated,
|
||||||
|
user,
|
||||||
|
setUser
|
||||||
|
}), [isAuthenticated, user])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthContext.Provider value={{ isAuthenticated, setIsAuthenticated, user, setUser }}>
|
<AuthContext.Provider value={value}>
|
||||||
{children}
|
{children}
|
||||||
</AuthContext.Provider>
|
</AuthContext.Provider>
|
||||||
)
|
)
|
||||||
@@ -81,16 +88,16 @@ function AppContent() {
|
|||||||
initAuth()
|
initAuth()
|
||||||
}, [setIsAuthenticated, setThemeLocal, setUser])
|
}, [setIsAuthenticated, setThemeLocal, setUser])
|
||||||
|
|
||||||
const loadHistory = async () => {
|
const loadHistory = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const history = await ChatService.listConversations()
|
const history = await ChatService.listConversations()
|
||||||
setConversations(history)
|
setConversations(history)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to load conversation history:", err)
|
console.error("Failed to load conversation history:", err)
|
||||||
}
|
}
|
||||||
}
|
}, [])
|
||||||
|
|
||||||
const handleAuthSuccess = async () => {
|
const handleAuthSuccess = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const userData = await AuthService.getMe()
|
const userData = await AuthService.getMe()
|
||||||
setUser(userData)
|
setUser(userData)
|
||||||
@@ -102,9 +109,9 @@ function AppContent() {
|
|||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
console.error("Failed to fetch user profile after login:", err)
|
console.error("Failed to fetch user profile after login:", err)
|
||||||
}
|
}
|
||||||
}
|
}, [setUser, setIsAuthenticated, setThemeLocal, loadHistory])
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
await AuthService.logout()
|
await AuthService.logout()
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
@@ -117,9 +124,9 @@ function AppContent() {
|
|||||||
setThreadMessages({})
|
setThreadMessages({})
|
||||||
setThemeLocal("light")
|
setThemeLocal("light")
|
||||||
}
|
}
|
||||||
}
|
}, [setIsAuthenticated, setUser, setThemeLocal])
|
||||||
|
|
||||||
const handleSelectConversation = async (id: string) => {
|
const handleSelectConversation = useCallback(async (id: string) => {
|
||||||
setSelectedThreadId(id)
|
setSelectedThreadId(id)
|
||||||
try {
|
try {
|
||||||
const msgs = await ChatService.getMessages(id)
|
const msgs = await ChatService.getMessages(id)
|
||||||
@@ -127,9 +134,9 @@ function AppContent() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to fetch messages:", err)
|
console.error("Failed to fetch messages:", err)
|
||||||
}
|
}
|
||||||
}
|
}, [])
|
||||||
|
|
||||||
const handleCreateConversation = async () => {
|
const handleCreateConversation = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const newConv = await ChatService.createConversation()
|
const newConv = await ChatService.createConversation()
|
||||||
setConversations(prev => [newConv, ...prev])
|
setConversations(prev => [newConv, ...prev])
|
||||||
@@ -138,18 +145,18 @@ function AppContent() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to create conversation:", err)
|
console.error("Failed to create conversation:", err)
|
||||||
}
|
}
|
||||||
}
|
}, [])
|
||||||
|
|
||||||
const handleRenameConversation = async (id: string, name: string) => {
|
const handleRenameConversation = useCallback(async (id: string, name: string) => {
|
||||||
try {
|
try {
|
||||||
const updated = await ChatService.renameConversation(id, name)
|
const updated = await ChatService.renameConversation(id, name)
|
||||||
setConversations(prev => prev.map(c => c.id === id ? updated : c))
|
setConversations(prev => prev.map(c => c.id === id ? updated : c))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to rename conversation:", err)
|
console.error("Failed to rename conversation:", err)
|
||||||
}
|
}
|
||||||
}
|
}, [])
|
||||||
|
|
||||||
const handleDeleteConversation = async (id: string) => {
|
const handleDeleteConversation = useCallback(async (id: string) => {
|
||||||
try {
|
try {
|
||||||
await ChatService.deleteConversation(id)
|
await ChatService.deleteConversation(id)
|
||||||
setConversations(prev => prev.filter(c => c.id !== id))
|
setConversations(prev => prev.filter(c => c.id !== id))
|
||||||
@@ -164,11 +171,11 @@ function AppContent() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Failed to delete conversation:", err)
|
console.error("Failed to delete conversation:", err)
|
||||||
}
|
}
|
||||||
}
|
}, [selectedThreadId])
|
||||||
|
|
||||||
const handleMessagesFinal = (id: string, messages: MessageResponse[]) => {
|
const handleMessagesFinal = useCallback((id: string, messages: MessageResponse[]) => {
|
||||||
setThreadMessages(prev => ({ ...prev, [id]: messages }))
|
setThreadMessages(prev => ({ ...prev, [id]: messages }))
|
||||||
}
|
}, [])
|
||||||
|
|
||||||
const queryParams = new URLSearchParams(window.location.search)
|
const queryParams = new URLSearchParams(window.location.search)
|
||||||
const externalError = queryParams.get("error")
|
const externalError = queryParams.get("error")
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { createContext, useContext, useEffect, useState } from "react"
|
import { createContext, useContext, useEffect, useState, useCallback, useMemo } from "react"
|
||||||
import { AuthService } from "@/services/auth"
|
import { AuthService } from "@/services/auth"
|
||||||
|
|
||||||
export type Theme = "light" | "dark"
|
export type Theme = "light" | "dark"
|
||||||
@@ -29,7 +29,7 @@ export function ThemeProvider({
|
|||||||
root.classList.add(theme)
|
root.classList.add(theme)
|
||||||
}, [theme])
|
}, [theme])
|
||||||
|
|
||||||
const setTheme = async (newTheme: Theme) => {
|
const setTheme = useCallback(async (newTheme: Theme) => {
|
||||||
setThemeState(newTheme)
|
setThemeState(newTheme)
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
try {
|
try {
|
||||||
@@ -38,18 +38,25 @@ export function ThemeProvider({
|
|||||||
console.error("Failed to sync theme to backend:", error)
|
console.error("Failed to sync theme to backend:", error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}, [isAuthenticated])
|
||||||
|
|
||||||
const setThemeLocal = (newTheme: Theme) => {
|
const setThemeLocal = useCallback((newTheme: Theme) => {
|
||||||
setThemeState(newTheme)
|
setThemeState(newTheme)
|
||||||
}
|
}, [])
|
||||||
|
|
||||||
const toggleTheme = () => {
|
const toggleTheme = useCallback(() => {
|
||||||
setTheme(theme === "light" ? "dark" : "light")
|
setTheme(theme === "light" ? "dark" : "light")
|
||||||
}
|
}, [theme, setTheme])
|
||||||
|
|
||||||
|
const value = useMemo(() => ({
|
||||||
|
theme,
|
||||||
|
setTheme,
|
||||||
|
setThemeLocal,
|
||||||
|
toggleTheme
|
||||||
|
}), [theme, setTheme, setThemeLocal, toggleTheme])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeContext.Provider value={{ theme, setTheme, setThemeLocal, toggleTheme }}>
|
<ThemeContext.Provider value={value}>
|
||||||
{children}
|
{children}
|
||||||
</ThemeContext.Provider>
|
</ThemeContext.Provider>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user