Как стилизовать компоненты с помощью makeStyles и при этом использовать методы жизненного цикла в пользовательском интерфейсе материалов?

Я получаю следующую ошибку всякий раз, когда пытаюсь использовать makeStyles() с компонентом с методами жизненного цикла:

Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app

Ниже приведен небольшой пример кода, выдающего эту ошибку. Другие примеры также назначают классы дочерним элементам. Я не могу найти в документации MUI ничего, что бы показывало другие способы использования makeStyles и возможности использования методов жизненного цикла.

    import React, { Component } from 'react';
    import { Redirect } from 'react-router-dom';

    import { Container, makeStyles } from '@material-ui/core';

    import LogoButtonCard from '../molecules/Cards/LogoButtonCard';

    const useStyles = makeStyles(theme => ({
      root: {
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
      },
    }));

    const classes = useStyles();

    class Welcome extends Component {
      render() {
        if (this.props.auth.isAuthenticated()) {
          return <Redirect to = "/" />;
        }
        return (
          <Container maxWidth = {false} className = {classes.root}>
            <LogoButtonCard
              buttonText = "Enter"
              headerText = "Welcome to PlatformX"
              buttonAction = {this.props.auth.login}
            />
          </Container>
        );
      }
    }

    export default Welcome;
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
161
0
183 706
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Привет, вместо использования хука API вы должны использовать API компонента более высокого порядка, как уже упоминалось здесь

Я изменю пример в документации, чтобы удовлетворить ваши потребности в компоненте класса.

import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/styles';
import Button from '@material-ui/core/Button';

const styles = theme => ({
  root: {
    background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
    border: 0,
    borderRadius: 3,
    boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
    color: 'white',
    height: 48,
    padding: '0 30px',
  },
});

class HigherOrderComponentUsageExample extends React.Component {
  
  render(){
    const { classes } = this.props;
    return (
      <Button className = {classes.root}>This component is passed to an HOC</Button>
      );
  }
}

HigherOrderComponentUsageExample.propTypes = {
  classes: PropTypes.object.isRequired,
};

export default withStyles(styles)(HigherOrderComponentUsageExample);

Здорово! Да, примеры не показывают этот метод. Однако мне любопытно, как бы вы использовали свой «HighOrderComponent» в другом файле .js. Вы можете использовать "withStyles(styles)(HigherOrderComponent)" для его создания, но как ссылаться на стили в файле .js HigherOrderComponent? И вы просто передаете реквизит обычным способом?

ThePartyTurtle 10.07.2019 21:53

@ThePartyTurtle, да, передача реквизита, как обычно. использование его будет в другом файле js, будет таким же, как и раньше. просто импортируйте HigherOrderComponent из HigherOrderComponent.js.

Vikas Kumar 11.07.2019 04:22

О, попался. Оказывается, я был сбит с толку, потому что не видел «экспорт по умолчанию со стилями (стили) (HigherOrderComponent);» синтаксиса, и я был сбит с толку, если это была функция, которую мне нужно было вызвать... Прошло много времени с тех пор, как я в последний раз разрабатывал веб-приложение, и я в последний раз использовал TypeScript, ха-ха. Спасибо за разъяснения.

ThePartyTurtle 11.07.2019 18:11

Я бегал по кругу с этой ошибкой и ошибкой invalid hook call. Спасибо, что направили меня в правильном направлении!

Kitson 21.07.2019 17:38

Это хорошо, но я могу как-то props в это вникнуть? Мне нужно сделать условные стили.

Jax-p 15.10.2019 17:33

@Jax-p посмотри мое решение

Matt Weber 26.10.2019 16:49

@VikasKumar Как при таком подходе я могу использовать тему приложения в своих стилях? Ф.э. отправить: {маржа: appTheme.spacing(3, 0, 2),},

Sergey Aldoukhov 08.11.2019 03:00

Не могли бы вы добавить типизацию для TypeScript, чтобы узнать, что содержит объект classes?

XCS 10.11.2019 13:58

Спасибо. Но проблема! Вы не использовали theme в своем styles теле (@SergeyAldoukhov это уже говорил). Когда я его использую, я получаю эту ошибку: "Невозможно прочитать свойство "X" неопределенного" и undefined точно theme! Я попробовал withStyles(styles(myDefinedMuiTheme))(...), и он работал правильно.

Mir-Ismaili 14.01.2020 22:46

@Китсон, Вероятно, вы использовали makeStyles()(styles = makeStyles(theme => ({...})). Кроме того, если вам нужен стиль, зависящий от темы, см. мой предыдущий комментарий.

Mir-Ismaili 14.01.2020 22:55

Это должен быть ответ.

Hasan Sefa Ozalp 04.02.2020 23:01

Привет, будущие гуглеры: пожалуйста, используйте import { withStyles } from '@material-ui/core/styles', а не так, как указано в ответе, из-за github.com/mui-org/material-ui/issues/…

Vaiden 11.07.2021 16:04
Ответ принят как подходящий

В итоге мы прекратили использование компонентов класса и создали функциональные компоненты используя useEffect() из API хуков для методов жизненного цикла. Это позволяет вам по-прежнему использовать makeStyles() с методами жизненного цикла без усложнения создания компонентов более высокого порядка. Что намного проще.

Пример:

import React, { useEffect, useState } from 'react';
import axios from 'axios';
import { Redirect } from 'react-router-dom';

import { Container, makeStyles } from '@material-ui/core';

import LogoButtonCard from '../molecules/Cards/LogoButtonCard';

const useStyles = makeStyles(theme => ({
  root: {
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    margin: theme.spacing(1)
  },
  highlight: {
    backgroundColor: 'red',
  }
}));

// Highlight is a bool
const Welcome = ({highlight}) => { 
  const [userName, setUserName] = useState('');
  const [isAuthenticated, setIsAuthenticated] = useState(true);
  const classes = useStyles();

  useEffect(() => {
    axios.get('example.com/api/username/12')
         .then(res => setUserName(res.userName));
  }, []);

  if (!isAuthenticated()) {
    return <Redirect to = "/" />;
  }
  return (
    <Container maxWidth = {false} className = {highlight ? classes.highlight : classes.root}>
      <LogoButtonCard
        buttonText = "Enter"
        headerText = {isAuthenticated && `Welcome, ${userName}`}
        buttonAction = {login}
      />
   </Container>
   );
  }
}

export default Welcome;

Я думаю, что для людей, использующих обновление React 16.8 Hooks или выше, переход на функцию — идеальное решение. В 16.8 функции могут обращаться к хукам состояния и жизненного цикла.

Tim 26.10.2019 01:04

Я сбит с толку, почему это получило отрицательные голоса. React ясно дал понять, что классы заменяются функциональными компонентами с хуками. http://reactjs.org/docs/…

Matt Weber 05.12.2019 20:13

Я не понизил голос, но очень сложно установить начальное состояние ленивым способом, используя xhr при использовании функционального компонента. С компонентом класса я могу установить начальное состояние на все, что захочу, затем использовать ajax, а затем setState после получения ответа. Я совершенно не знаю, как сделать это красиво с помощью функции.

mlt 10.12.2019 01:00

Вы бы использовали useEffect. В приведенном выше случае вы устанавливаете начальное состояние userName в пустую строку, затем после выполнения вызова API, обеспечивающего useEffect, вы будете использовать setUserName(response). Я добавлю пример выше и ссылку на статью с дополнительной информацией об использовании useEffect для методов жизненного цикла. dev.to/прототип/…

Matt Weber 10.12.2019 19:56

За это проголосовали против, потому что функциональное программирование отстой в реальных приложениях, которым нужна архитектура. Это усиливает и без того распространенную тенденцию js-программистов делать большие какашки спагетти-кода, которые очень, очень трудно читать/следовать и которые невозможно разделить на разумные компоненты. Если React продвигается таким образом, они совершают большую ошибку, и я не буду следовать за ними.

RickyA 12.12.2019 10:46

Вместо преобразования класса в функцию простым шагом было бы создать функцию, включающую jsx для компонента, который использует «классы», в вашем случае <container></container>, а затем вызвать эту функцию внутри возврата класса render( ) в качестве тега. Таким образом, вы перемещаете крючок на функцию из класса. Это сработало отлично для меня. В моем случае это был <table>, который я переместил в функцию TableStmt снаружи и назвал эту функцию внутри рендера как <TableStmt/>

useStyles — это хук React, который предназначен для использования в функциональных компонентах и ​​не может использоваться в компонентах класса.

Из реакции:

Hooks let you use state and other React features without writing a class.

Также вы должны вызвать useStyles хук внутри вашей функции как;

function Welcome() {
  const classes = useStyles();
...

Если вы хотите использовать хуки, вот ваш краткий компонент класса, преобразованный в функциональный компонент;

import React from "react";
import { Container, makeStyles } from "@material-ui/core";

const useStyles = makeStyles({
  root: {
    background: "linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)",
    border: 0,
    borderRadius: 3,
    boxShadow: "0 3px 5px 2px rgba(255, 105, 135, .3)",
    color: "white",
    height: 48,
    padding: "0 30px"
  }
});

function Welcome() {
  const classes = useStyles();
  return (
    <Container className = {classes.root}>
      <h1>Welcome</h1>
    </Container>
  );
}

export default Welcome;

? в ↓ CodeSandBox ↓

Edit React hooks

Я использовал withStyles вместо makeStyle

БЫВШИЙ :

import { withStyles } from '@material-ui/core/styles';
import React, {Component} from "react";

const useStyles = theme => ({
        root: {
           flexGrow: 1,
         },
  });

class App extends Component {
       render() {
                const { classes } = this.props;
                return(
                    <div className = {classes.root}>
                       Test
                </div>
                )
          }
} 

export default withStyles(useStyles)(App)

Да, при использовании заданных компонентов MUI это решение подходило именно для того сценария, который у нас был. Должен быть принятый ответ

dipan66 22.03.2021 16:31

Это лучший ответ. Должен занять первое место

Pedro Lopez 04.08.2021 15:10

Еще одно решение можно использовать для компонентов класса - просто переопределите свойства темы MUI по умолчанию с помощью MuiThemeProvider. Это даст больше гибкости по сравнению с другими методами — вы можете использовать более одного MuiThemeProvider внутри вашего родительского компонента.

простые шаги:

  1. импортируйте MuiThemeProvider в компонент вашего класса
  2. импортировать createMuiTheme в ваш компонент класса
  3. создать новую тему
  4. оберните целевой компонент MUI, который вы хотите стилизовать, с помощью MuiThemeProvider и вашей пользовательской темы

пожалуйста, проверьте этот документ для более подробной информации: https://material-ui.com/customization/theming/

import React from 'react';
import PropTypes from 'prop-types';
import Button from '@material-ui/core/Button';

import { MuiThemeProvider } from '@material-ui/core/styles';
import { createMuiTheme } from '@material-ui/core/styles';

const InputTheme = createMuiTheme({
    overrides: {
        root: {
            background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
            border: 0,
            borderRadius: 3,
            boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
            color: 'white',
            height: 48,
            padding: '0 30px',
        },
    }
});

class HigherOrderComponent extends React.Component {

    render(){
        const { classes } = this.props;
        return (
            <MuiThemeProvider theme = {InputTheme}>
                <Button className = {classes.root}>Higher-order component</Button>
            </MuiThemeProvider>
        );
    }
}

HigherOrderComponent.propTypes = {
    classes: PropTypes.object.isRequired,
};

export default HigherOrderComponent;

Вы вызываете хук useStyles вне функции. Вот почему

В дополнение к ответу, предоставленному @vikas-kumar, также можно использовать props, которые устанавливаются для стилизованного компонента, например.

const styles = theme => ({
  root: {
    background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
    border: 0,
    borderRadius: 3,
    boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
    color: 'white',
    height: props => props.height,
    padding: '0 30px',
  },
});

Таким образом, height для применяемого стиля может регулироваться

<HigherOrderComponentUsageExample height = {48}/>

Дополнительные сведения о динамическом стиле см. здесь: https://material-ui.com/styles/basics/#adapting-the-higher-order-component-api.

Другие вопросы по теме