🎨 refactor(ui): harden iframe messaging
- useHeaderBar.js - Wrap handlers with useCallback (logout, language/theme toggle, mobile menu) - Add null checks and try/catch around iframe postMessage (theme & language) - Keep minimal effect deps; remove unused StatusContext dispatch - Logo preload effect safe and scoped to `logo` - index.jsx - No functional changes; locale memoization remains stable Chore: - Lint clean; no runtime warnings - Verified no render loops or performance regressions
This commit is contained in:
@@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
For commercial licensing, please contact support@quantumnous.com
|
For commercial licensing, please contact support@quantumnous.com
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState, useEffect, useContext } from 'react';
|
import { useState, useEffect, useContext, useCallback } from 'react';
|
||||||
import { useNavigate, useLocation } from 'react-router-dom';
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { UserContext } from '../../context/User';
|
import { UserContext } from '../../context/User';
|
||||||
@@ -68,9 +68,14 @@ export const useHeaderBar = ({ onMobileMenuToggle, drawerOpen }) => {
|
|||||||
|
|
||||||
// Send theme to iframe
|
// Send theme to iframe
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const iframe = document.querySelector('iframe');
|
try {
|
||||||
if (iframe) {
|
const iframe = document.querySelector('iframe');
|
||||||
iframe.contentWindow.postMessage({ themeMode: actualTheme }, '*');
|
const cw = iframe && iframe.contentWindow;
|
||||||
|
if (cw) {
|
||||||
|
cw.postMessage({ themeMode: actualTheme }, '*');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Silently ignore cross-origin or access errors
|
||||||
}
|
}
|
||||||
}, [actualTheme]);
|
}, [actualTheme]);
|
||||||
|
|
||||||
@@ -78,9 +83,14 @@ export const useHeaderBar = ({ onMobileMenuToggle, drawerOpen }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleLanguageChanged = (lng) => {
|
const handleLanguageChanged = (lng) => {
|
||||||
setCurrentLang(lng);
|
setCurrentLang(lng);
|
||||||
const iframe = document.querySelector('iframe');
|
try {
|
||||||
if (iframe) {
|
const iframe = document.querySelector('iframe');
|
||||||
iframe.contentWindow.postMessage({ lang: lng }, '*');
|
const cw = iframe && iframe.contentWindow;
|
||||||
|
if (cw) {
|
||||||
|
cw.postMessage({ lang: lng }, '*');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Silently ignore cross-origin or access errors
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -91,32 +101,32 @@ export const useHeaderBar = ({ onMobileMenuToggle, drawerOpen }) => {
|
|||||||
}, [i18n]);
|
}, [i18n]);
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
const logout = async () => {
|
const logout = useCallback(async () => {
|
||||||
await API.get('/api/user/logout');
|
await API.get('/api/user/logout');
|
||||||
showSuccess(t('注销成功!'));
|
showSuccess(t('注销成功!'));
|
||||||
userDispatch({ type: 'logout' });
|
userDispatch({ type: 'logout' });
|
||||||
localStorage.removeItem('user');
|
localStorage.removeItem('user');
|
||||||
navigate('/login');
|
navigate('/login');
|
||||||
};
|
}, [navigate, t, userDispatch]);
|
||||||
|
|
||||||
const handleLanguageChange = (lang) => {
|
const handleLanguageChange = useCallback((lang) => {
|
||||||
i18n.changeLanguage(lang);
|
i18n.changeLanguage(lang);
|
||||||
};
|
}, [i18n]);
|
||||||
|
|
||||||
const handleThemeToggle = (newTheme) => {
|
const handleThemeToggle = useCallback((newTheme) => {
|
||||||
if (!newTheme || (newTheme !== 'light' && newTheme !== 'dark' && newTheme !== 'auto')) {
|
if (!newTheme || (newTheme !== 'light' && newTheme !== 'dark' && newTheme !== 'auto')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setTheme(newTheme);
|
setTheme(newTheme);
|
||||||
};
|
}, [setTheme]);
|
||||||
|
|
||||||
const handleMobileMenuToggle = () => {
|
const handleMobileMenuToggle = useCallback(() => {
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
onMobileMenuToggle();
|
onMobileMenuToggle();
|
||||||
} else {
|
} else {
|
||||||
toggleCollapsed();
|
toggleCollapsed();
|
||||||
}
|
}
|
||||||
};
|
}, [isMobile, onMobileMenuToggle, toggleCollapsed]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// State
|
// State
|
||||||
|
|||||||
Reference in New Issue
Block a user