【问题标题】:Custom components based on Material UI and React基于 Material UI 和 React 的自定义组件
【发布时间】:2020-09-24 10:37:49
【问题描述】:

我一直在使用 Material UI 组件来创建自己的自定义可重用组件包。这是一个位于包中的自定义组件示例,仅用于演示目的:

import PropTypes from 'prop-types';
import {makeStyles, ThemeProvider} from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';

const useStyles = makeStyles({
    'buttonPackage': {
        backgroundColor: theme.palette.primary.main,
        color: 'rgba(12, 0, 0, 1)',
        minWidth: '147px',
        padding: '8px 8px 9px 8px',
    },
});

const CustomButton = ({children, theme}) => {
    const classes = useStyles();

    return (
        <ThemeProvider theme={theme}>
            <Button className={classes.buttonPackage}>
                {children}
            </Button>
        </ThemeProvider>
    );
};

export default CustomButton;

如果没有包裹在 ThemeProvider 中,则全局主题变量不会应用于自定义组件,尽管整个应用程序都包裹在 ThemeProvider 中。

当我尝试在 React 应用程序部分中对具有非常特定样式且不能被视为可重用的组件进行进一步自定义时,就会出现问题。这是一个 React 视图组件的示例,包含来自包的自定义组件和在视图组件内部自定义的组件,这只是一个演示代码:

import React from 'react';
import {makeStyles} from '@material-ui/core/styles';
import {Button} from '@material-ui/core';
import {CustomButton, theme} from '@name-of-my-package/ui-components';

const useStyles = makeStyles(theme => ({
    buttonApp: {
        height: '100%',
    },
}));

const GenericView = () => {
    const classes = useStyles();

    return (
        <React.Fragment>
            <CustomButton theme={theme}>Button imported from package</CustomButton>
            <Button className={classes.buttonApp}/>Button customized in app</Button>
        </React.Fragment>
    );
};

export default GenericView;

在开发中使用时,一切正常。在生产中使用时,一些样式属性在 package 和 react app 中自定义的组件之间共享。捆绑了有效的类,但不会在页面首次加载时应用。当您在应用程序周围单击时(例如,从导航组件更改路线),将应用有效样式并且一切看起来都正常。我注意到在可重用组件包中自定义的组件和在 React 应用程序中自定义的组件之间存在样式重叠。

这种方法是否可行,拥有自定义组件包并在应用部分进行进一步自定义,包括主题?

更新 16.06.2020.:目前,解决方案是在项目的应用程序部分完全不使用 JSS 样式。在项目的应用程序部分中创建的 JSS 样式类与在可重用组件包中创建的 JSS 样式类的名称重叠,这会导致问题。

【问题讨论】:

    标签: reactjs material-ui


    【解决方案1】:

    这个问题的解决方案包括几个步骤:

    1. 更改了应用程序的 React 部分的 Webpack 配置以包含 Material UI 别名。这样,React 应用程序中使用的 Material UI 组件和可重用组件包中的组件都使用相同的 React 上下文实例,Material UI 将其用于主题目的。下面是一个需要添加的 webpack 别名属性示例:
    alias: {
        'react': require.resolve('react'),
        'react-dom': require.resolve('react-dom'),
        '@material-ui/core': path.resolve('./node_modules/@material-ui/core'),
        '@material-ui/core/styles': path.resolve('./node_modules/@material-ui/core/styles'),
        '@material-ui/lab': path.resolve('./node_modules/@material-ui/lab'),
        '@material-ui/icons': path.resolve('./node_modules/@material-ui/icons'),
        '@material-ui/pickers': path.resolve('./node_modules/@material-ui/pickers'),
    },
    
    1. 更改了使用组件时的导入方式。如果您导入组件,请使用 webpack 别名中定义的路径。例如,不要像这样导入 Button 组件:
    import Button from '@material-ui/core/Button';
    

    像这样导入它:

    import {Button} from '@material-ui/core';
    
    1. 在可重用组件包汇总配置中,材质 UI 包已设置为外部依赖项。这样,可重用组件应该使用安装在应用程序的 React 部分中的 Material UI:
    external: [
        'react',
        'react-dom',
        '@material-ui/core',
        '@material-ui/core/styles',
        '@material-ui/lab',
        '@material-ui/icons',
        '@material-ui/pickers',
    ],
    
    1. 又增加了一件事,在可重用组件包中,创建了一个新组件,MyStylesProvider。此包装器为为可重用组件生成的样式类添加了唯一前缀。这样,在生产中就不会出现样式重叠:
    import React from 'react';
    import {
        StylesProvider,
        createGenerateClassName,
    } from '@material-ui/core/styles';
    
    const generateClassName = createGenerateClassName({
        productionPrefix: 'so', // change this string to whatever you like
        seed: 'StackOverflow', // change this string to whatever you like
    });
    
    const MyStylesProvider = props => {
        return (
            <StylesProvider
                injectFirst={true}
                generateClassName={generateClassName}
            >
                {props.children}
            </StylesProvider>
        );
    };
    
    export default MyStylesProvider;
    
    

    然后我在应用程序的 React 部分中使用它,如下所示:

    import React from 'react';
    import {theme, MyStylesProvider} from '@package/reusable-components';
    import {renderRoutes} from 'react-router-config';
    import {ThemeProvider} from '@material-ui/core/styles';
    import {CssBaseline} from '@material-ui/core';
    
    const AppView = ({route}) => (
        <ThemeProvider theme={theme}>
            <CssBaseline />
            <MyStylesProvider>
                {renderRoutes(route.routes)}
            </MyStylesProvider>
        </ThemeProvider>
    );
    
    export default AppView;
    

    这可能不是最理想的方法,但它确实有效。我很想听听其他人在导出可重用 Material UI 组件方面的意见和经验。

    【讨论】:

    • 这是您的最终解决方案吗?还是您找到了“更简单”的方法?我在提供样式化组件时遇到了类似的问题;
    • 这是我可以使它与我的项目设置一起工作的唯一方法。
    猜你喜欢
    • 2018-11-23
    • 1970-01-01
    • 2017-09-08
    • 2020-09-15
    • 2018-11-24
    • 2020-01-16
    • 2020-05-23
    • 2021-03-23
    • 2017-01-23
    相关资源
    最近更新 更多