Next.js+Material UI点击触发API调用后运行时动态切换全局主题咨询
解决方案
核心逻辑说明
- MUI 内置的
ThemeProvider仅负责向下传递主题配置,不提供更新主题的内置方法,所以根组件用useState存储主题状态是必须的,但完全可以不用自定义 Context,直接通过 Next.js 页面 props 传递修改方法即可。 - 不能在普通异步函数内部调用
useContext是 React Hook 的硬性调用规则限制,Hook 只能在函数组件顶层调用,所以需要先在组件内拿到更新方法,再传入异步函数使用。
修改后的实现代码
1. _app.ts 去掉自定义 Context
export default function MyApp(props: any) { const { Component, pageProps } = props; const [theme, setTheme] = useState(muiTheme); React.useEffect(() => { // Remove the server-side injected CSS. const jssStyles = document.querySelector('#jss-server-side'); if (jssStyles && (jssStyles.parentElement !== null)) { jssStyles.parentElement.removeChild(jssStyles); } }, []); return ( <React.Fragment> <Head> <title>My page</title> <meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" /> </Head> <ThemeProvider theme={theme}> <CssBaseline /> {/* 直接把setTheme传给所有页面组件 */} <Component {...pageProps} setTheme={setTheme} /> </ThemeProvider> </React.Fragment> ); }
2. 页面组件调整异步逻辑
// 页面组件内代码 export default function RoomPage(props) { // 组件顶层先拿到setTheme方法 const { setTheme } = props; // 异步方法定义在组件内部,直接调用拿到的setTheme async function updateRoom(room: any, flowActionId: any) { try { const response = await axios.put(`/api/room/${room.id}`, { flow_action_id: Number(flowActionId) }); // 从接口返回结果取自定义颜色 const newPrimaryColor = response.data.themeColor || '#123123'; const leTheme = createTheme({ palette: { primary: { main: newPrimaryColor, } }, }); // 直接更新全局主题 setTheme(leTheme); } catch (error) { console.error(error); } } return ( <Button onClick={() => updateRoom(room, flowActionId)} // 其他按钮属性 /> ) }
补充说明
如果项目组件层级深,不想一层层传递 setTheme,你原来的自定义 Context 方案是 React 全局状态共享的标准实现,并不算冗余,更适合中长期维护。如果不想用 Context 也不想逐层传参,可以引入轻量状态管理库(比如 Zustand)存储 theme 状态,代码会更简洁。
内容的提问来源于stack exchange,提问作者Electrik Bunny
相关产品推荐
相关产品推荐

