React-Router 和 Material-UI:根据路由应用自定义主题

Posted

技术标签:

【中文标题】React-Router 和 Material-UI:根据路由应用自定义主题【英文标题】:React-Router and Material-UI: Applying custom themes depending on route 【发布时间】:2020-08-19 08:14:32 【问题描述】:

我刚开始使用Material-UI。我知道我可以在一个组件中使用createStyles 来设置它的样式,并且我也可以使用createMuiTheme 来创建一个全局主题。我想做的是以createMuiTheme 的方式创建一个主题,并包含许多不同的原色和二次色组合。

我正在制作一个显示 NHL 球队统计数据的网络应用。我正在动态创建一个组件,该组件根据React-Router 和页面 URL 显示团队统计信息。例如,如果用户转到/rangers,则页面显示纽约游骑兵队的统计数据,如果用户转到/bruins,则页面显示波士顿棕熊队的统计数据等...

我实现这一点的方法是使用 React-Router's useLocation 函数。当用户导航到 /rangers 时,我使用 useLocation 从 URL 中获取 rangers,然后将团队名称放入 GET 请求中,以便请求和显示 Rangers 统计数据。

我想做的是创建一个Material-UI 主题,以根据它所在的团队在页面上动态设置主要颜色和次要颜色。例如,当用户位于/rangers(流浪者队颜色)时,我想将主色设置为蓝色,将辅助色设置为红色。如果用户要导航到/bruins,那么我想将主要颜色和次要颜色设置为 Bruins 团队颜色(黑色和金色)。

const Theme = createMuiTheme(
    palette: 
        primary: 
          // When at '/rangers` set primary color to Rangers blue
          rangers: '#0038a8',
          // When at '/bruins` set primary color to Bruins gold
          bruins: '#fcb514'
        ,
        secondary: 
          // When at '/rangers` set secondary color to Rangers red
          rangers: '#ce1126',
          // When at '/bruins` set secondary color to Bruins black
          bruins: '#111'
        
    );

有没有办法根据React-Router 所在的页面在Material-UI 中动态设置主题颜色,即导航到/rangers 设置Rangers 颜色并导航到/bruins 设置主题中的Bruins 颜色?我想为此使用useLocation,其方式与我执行 GET 请求的方式类似。

我有 31 个不同的团队/页面,因此动态执行此操作比手动创建具有不同样式的 31 个不同组件更有效。

【问题讨论】:

【参考方案1】:

您可以创建一种修改主题的方法。在这种情况下,我们需要了解如何在组件树中渲染组件。但是,我做了一个简单的操作示例,您可以将其作为实现的基础。

从技术上讲,您需要使用 Context Api 创建自定义主题提供程序,以便可以从应用程序中的任何位置访问它。这样我们就可以在任何组件中修改主题。

export function ThemeProvider(props) 
  const  children  = props;

  const [themeOptions, dispatch] = React.useReducer((state, action) => 
    switch (action.type) 
      case "CHANGE":
        return 
          ...state,
          colors: action.payload.colors || "DEFAULT"
        ;
      default:
        throw new Error(`Unrecognized type $action.type`);
    
  , themeInitialOptions);

  const  colors  = themeOptions;
  const theme = React.useMemo(() => 
    let palette;

    switch (colors) 
      case "RANGERS":
        palette = 
          primary:  main: "#0038a8" ,
          secondary:  main: "#ce1126" 
        ;
        break;
      case "BRUINS":
        palette = 
          primary:  main: "#fcb514" ,
          secondary:  main: "#111" 
        ;
        break;
      default:
        palette = 
          primary:  main: "#673ab7" ,
          secondary:  main: "#111" 
        ;
        break;
    

    const nextTheme = createMuiTheme( palette );
    return nextTheme;
  , [colors]);

  return (
    <MuiThemeProvider theme=theme>
      <DispatchContext.Provider value=dispatch>
        children
      </DispatchContext.Provider>
    </MuiThemeProvider>
  );

然后为更改提供一个通用入口点。

export function useChangeTheme() 
  const dispatch = React.useContext(DispatchContext);
  return React.useCallback(
    themeOptions => dispatch( type: "CHANGE", payload: themeOptions ),
    [dispatch]
  );

最后,我们可以在 React 树顶部的组件上以这种方式使用它。

  const changeTheme = useChangeTheme();
  const location = useLocation();

  React.useEffect(() => 
    let path = location && location.pathname.split("/");
    let team = path && path[1];
    changeTheme( colors: team.toUpperCase() );
  , [changeTheme, location]);

希望对你有帮助。

【讨论】:

非常好的答案! 感谢您的详细回复。这绝对给了我一个很好的起点。

以上是关于React-Router 和 Material-UI:根据路由应用自定义主题的主要内容,如果未能解决你的问题,请参考以下文章

如何用 redux 和 react-router 组织状态?

React-Router面试题汇总

React-router学习笔记

react-router@4.0 使用和源码解析

[react-router] React-Router怎么设置重定向?

React-Router 动画 Animation