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

时间:2020-05-04 19:31:03

标签: css reactjs react-router material-ui react-router-dom

我刚开始使用Material-UI。我知道我可以在组件内使用createStyles对其进行样式设置,也可以使用createMuiTheme创建全局主题。我想做的是以createMuiTheme方式创建主题,并包含许多不同的原色和第二色组合。

我正在制作一个显示NHL团队统计信息的Web应用程序。我正在动态创建一个基于React-Router和页面URL显示团队统计信息的组件。例如,如果用户转到/rangers,则该页面显示“纽约流浪者”的统计信息;如果用户转到/bruins,则该页面显示“波士顿熊”的统计信息,等等。

我完成此操作的方法是使用React-Router's useLocation函数。当用户导航到/rangers时,我使用useLocation从URL中抓取rangers,然后将团队名称放入GET请求中,以便请求并显示Rangers统计信息。

我想做的是创建一个Material-UI主题,以根据页面所属的团队在页面上动态设置原色和副色。例如,当用户在/rangers时,我想将原色设置为蓝色,将次要颜色设置为红色(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'
        }
    });

是否有一种方法可以根据Material-UI所在的页面在React-Router中动态设置主题颜色,即导航至/rangers设置流浪者的颜色并导航至/bruins的设置主题中的棕褐色?我想以与我执行GET请求类似的方式使用useLocation

我有31个不同的团队/页面,所以动态地执行此操作比手动创建31个具有不同样式的不同组件要有效得多。

1 个答案:

答案 0 :(得分:2)

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

Edit Button

从技术上讲,您需要使用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]);

我希望这会有所帮助。