Пользовательские стили компонентов React перезаписываются стилем Material-UI

СВЯЗАННЫЙ ВОПРОС НАД: Стили перезаписываются стилем Material-UI

Я создаю библиотеку компонентов поверх UI материала. Используя JSS, я хотел бы иметь возможность передавать стили своим пользовательским компонентам. Однако у меня возникают проблемы с тем, что корневые стили material-ui имеют более высокую специфичность, чем то, что я передаю. Я попытался перезаписать стили по умолчанию компонентов material-ui синтаксисом classes, но он просто создает другой класс с аналогичным именем и более высокая специфичность (makeStyles-root-51).

Инструменты разработчика Chrome

Потребление пользовательского компонента:

import React from 'react';
import {gSelect} from 'g-react-component-library/dist'
import {createUseStyles} from 'react-jss'

const useStyles = createUseStyles({
    gSelect: {margin: "15px"},
    example: {float: "left", display: "inline-block", whiteSpace: 'nowrap', verticalAlign: 'top'}
});

function App() {

    const classes = useStyles();
    return (
        <div className={classes.example}>
            <div className={classes.separator}>
                <div>Selects:</div>
                <gSelect default={1} classes={{gSelect: classes.gSelect}} callback={(e)=>{console.log(`${e} selected`)}} options={[1,2,3,4]}/>
                <gSelect default={'One'} classes={{gSelect: classes.gSelect}} callback={(e)=>{console.log(`${e} selected`)}} options={["One", "Two", "Three", "Four"]}/>
            </div>
        </div>
    );
}

export default App;

Фактический пользовательский компонент:

import React, {useState} from 'react';
import {Button, Select, FormControl, MenuItem, InputLabel} from '@material-ui/core'
import {makeStyles} from '@material-ui/styles'
import PropTypes from 'prop-types'

const gSelect = (props) => {

    const [value, setValue] = useState();

    const handleChange = event => {
        props.callback(event.target.value);
        setValue(event.target.value);
    };

    const useStyles = makeStyles({
        select: {
            border: 'solid #33333366 1px',
            color: 'rgba(51, 51, 51, 0.66)',
            fontWeight: '700',
            backgroundColor: 'white',
            outline: 'none',
            borderRadius: '5px',
            textAlign: 'left',
            width: '300px',
            position: 'relative',
        },
        root: {

        }
    });

    const classes = useStyles(props);
    return (
        <FormControl classes={{root: classes.gSelect}}>
        <InputLabel id="demo-simple-select-label">{props.default}</InputLabel>
        <Select value={value} onChange={handleChange} className={classes.select}>
            {props.options.map((option, index) => {
                return <MenuItem key={`${option}_${index}`} value={option}>{option}</MenuItem>
            })}
        </Select>
        </FormControl>
    )
};

gSelect.propTypes = {
    callback: PropTypes.func.isRequired,
    options: PropTypes.array.isRequired,
    default: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.number
    ]).isRequired,
    disabled: PropTypes.bool,
    width: PropTypes.string
};

module.exports = {
    gSelect
};

person Jonny B    schedule 07.02.2020    source источник
comment
Вы не должны создавать ловушку useStyles внутри компонента, это противоречит цели.   -  person Adam    schedule 07.02.2020
comment
Как так? Если я хочу создать библиотеку пользовательских компонентов поверх UI материала, вам нужно каким-то образом придать ей пользовательские стили.   -  person Jonny B    schedule 07.02.2020
comment
Я объяснил в своем ответе ниже - суть вопроса в том, что classes должен проходить через реквизиты, и вы должны передать эти реквизиты в свой хук - материальный пользовательский интерфейс выполняет всю магию конкатенации, передаваемую в классах, в ваш собственный. Лично я предпочитаю withStyles hoc, потому что вся эта логика обрабатывается автоматически для вас, и вам не нужно беспокоиться о том, чтобы забыть передать свои реквизиты в хук useStyles.   -  person Adam    schedule 07.02.2020
comment
@ Адам, я внес изменения, которые вы предложили. Однако я все еще сталкиваюсь с проблемой, когда корневой класс MUI имеет более высокую специфичность и перезаписывает мои поля. Кроме того, я сильно сократил код из источника для этого вопроса, и поэтому, если есть какие-то вопиющие проблемы, это, вероятно, то, что вы видите. В моем первоначальном вопросе я понял, что стиль, который я передавал компоненту, был таким же, как стиль, который он использовал внутри для настройки выбора. Это было просто ошибкой, когда я редактировал вопрос. Я должен был исправить это в обновленном вопросе.   -  person Jonny B    schedule 07.02.2020
comment
Если бы вы могли опубликовать codepen / stackblitz, демонстрирующий проблему (не обязательно, чтобы это был точный код, просто что-то подобное), это было бы долгим путем.   -  person Adam    schedule 07.02.2020
comment
Не пытайтесь использовать существующий код, сделайте небольшую копию, которая использует материальный интерфейс и пару фиктивных компонентов, в то время как вы делаете это, вы даже можете решить свою проблему!   -  person Adam    schedule 07.02.2020
comment
Трудно объяснить, проверьте разницу между вашим и тем, что я сделал здесь: codeandbox.io/s/ material-demo-so5yk. Задайте любые вопросы, которые вам нравятся. Несколько месяцев назад я потратил некоторое время на изучение стилевого решения material-ui, это потрясающе, но пока вы действительно не разобрались с ним, вам придется с ним бороться. (РЕДАКТИРОВАТЬ: добавлены подробные комментарии, надеюсь, чтобы прояснить, что происходит).   -  person Adam    schedule 08.02.2020
comment
Это не ошибки, это предупреждения, отображаемые с console.error. На самом деле это это предупреждение. Пользовательский интерфейс Material предоставляет эти предупреждения, потому что они думают, что это вызвано тем, что разработчики не знают, что они делают (в большинстве случаев они правы). Но если вы знаете, как работает слияние классов, их можно игнорировать. Кстати, эта песочница должна показать вам, как это исправить: codeandbox.io/s/material- demo-udlfh   -  person Adam    schedule 11.02.2020
comment
@Adam, я обновил свой пример песочницы кода, чтобы использовать className, и это сработало. Я пробовал это изначально, но думаю, что из-за того, что я использовал createUseStyles вместо makeStyles, это не сработало. Тем не менее, ваша помощь была действительно неоценимой, чтобы найти это решение, спасибо! Кроме того, я столкнулся с очень похожей, но другой проблемой и создал еще один вопрос. Я ни в коем случае не жду вашей помощи, но если вы хотите попробовать ее, будь моим гостем. Спасибо еще раз! stackoverflow.com/questions/60179584 /   -  person Jonny B    schedule 12.02.2020


Ответы (2)


Ты делаешь это неправильно. GSelect должен получать такие классы:

В приложении:

<GSelect default={1} classes={{gSelect:classes.gSelect}} callback={(e)=>{console.log(`${e} selected`)}} options={[1,2,3,4]}/>

Затем в GSelect:

const useStyles = createStyles(...your styles); // call this hook factory outside your render

const GSelect = props => {
   const classes = useStyles(props) <- props contains an object called classes with a property gselect that gets merged into yours

   <Select value={value} onChange={handleChange} classes={{root:classes.gSelect}}>
}

РЕДАКТИРОВАТЬ: Что касается моего первоначального комментария о создании вашего крючка вне вашего компонента, я имел в виду следующее:


// move this outside of your render
    const useStyles = createUseStyles({
        gSelect: {margin: "15px"},
        separator: {marginTop: "15px"},
        example: {float: "left", display: "inline-block", whiteSpace: 'nowrap', verticalAlign: 'top'}
    });

function App() {
    // use it inside of your render
    const classes = useStyles();
    ...
}

Прочтите этот и два следующих раздела, через некоторое время он щелкнет: https://material-ui.com/styles/advanced/#overriding-styles-classes-prop

person Adam    schedule 07.02.2020

Решение передать стили в пользовательский компонент в конце концов оказалось очень простым. Хотя @Adam ответил на мой вопрос так, как он был сформулирован, решение, к которому я пришел, выглядит следующим образом:

Потребление пользовательского компонента:

import React from 'react';
import {gSelect} from 'g-react-component-library/dist'
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles ({
    gSelect: {margin: "15px"},
    example: {float: "left", display: "inline-block", whiteSpace: 'nowrap', verticalAlign: 'top'}
});

function App() {

    const classes = useStyles();
    return (
        <div className={classes.example}>
            <div className={classes.separator}>
                <div>Selects:</div>
                <gSelect default={1} className={classes.gSelect} callback={(e)=>{console.log(`${e} selected`)}} options={[1,2,3,4]}/>
                <gSelect default={'One'} className={classes.gSelect} callback={(e)=>{console.log(`${e} selected`)}} options={["One", "Two", "Three", "Four"]}/>
            </div>
        </div>
    );
}

export default App;

Вышеуказанные исправления включают:

  • переключил createUseStyles на makeStyles с @material-ui/core/styles
  • использовал className={classes.gSelect} вместо classes={{root: ...}}

Фактический пользовательский компонент:

import React, {useState} from 'react';
import {Button, Select, FormControl, MenuItem, InputLabel} from '@material-ui/core'
import {makeStyles} from '@material-ui/styles'
import PropTypes from 'prop-types'

const gSelect = (props) => {

const [value, setValue] = useState();

const handleChange = event => {
    props.callback(event.target.value);
    setValue(event.target.value);
};

const useStyles = makeStyles({
    select: {
        border: 'solid #33333366 1px',
        color: 'rgba(51, 51, 51, 0.66)',
        fontWeight: '700',
        backgroundColor: 'white',
        outline: 'none',
        borderRadius: '5px',
        textAlign: 'left',
        width: '300px',
        position: 'relative',
    }
});

const classes = useStyles();
return (
    <FormControl className={props.className}>
    <InputLabel id="demo-simple-select-label">{props.default}</InputLabel>
    <Select value={value} onChange={handleChange} className={classes.select}>
        {props.options.map((option, index) => {
            return <MenuItem key={`${option}_${index}`} value={option}>{option}</MenuItem>
        })}
    </Select>
    </FormControl>
)
};

gSelect.propTypes = {
    callback: PropTypes.func.isRequired,
    options: PropTypes.array.isRequired,
    default: PropTypes.oneOfType([
        PropTypes.string,
        PropTypes.number
    ]).isRequired,
    disabled: PropTypes.bool,
    width: PropTypes.string
};

module.exports = {
    gSelect
};

Вышеуказанные исправления включают:

  • использовался className={props.className} на <FormControl>
person Jonny B    schedule 12.02.2020