如何使用makeStyles为组件设置样式,并在Material UI中仍然具有生命周期方法?


116

每当我尝试使用makeStyles()具有生命周期方法的组件时,都会出现以下错误:

无效的挂接调用。挂钩只能在功能组件的主体内部调用。可能由于以下原因之一而发生:

  1. 您可能使用了不匹配的React和渲染器版本(例如React DOM)
  2. 您可能正在违反钩子规则
  3. 您可能在同一应用中拥有多个React副本

下面是产生此错误的代码的一个小示例。其他示例也将类分配给子项。我在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;

Answers:


168

您好,而不是使用hook 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 HigherOrderComponent extends React.Component {

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

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

export default withStyles(styles)(HigherOrderComponent);

4
我一直在绕着这个错误和invalid hook call错误跑来跑去-感谢您向正确的方向发展!!
杰森

1
@ Jax-p看到我的解决方案
Matt Weber

4
@VikasKumar通过这种方法,如何在我的样式中使用应用程序主题?Fe提交:{margin:appTheme.spacing(3,0,2),},
Sergey Aldoukhov,

1
谢谢。但是有问题!您没有themestyles体内使用(@SergeyAldoukhov已经说过了)。当我使用它,我得到这个错误:“无法读取的未定义的属性‘X’”undefinedtheme准确!我试过了withStyles(styles(myDefinedMuiTheme))(...),它工作正常。
Mir-Ismaili

1
@Kitson,可能您已使用makeStyles() styles = makeStyles(theme => ({...})另外,如果您想要主题相关的样式,请参阅我之前的评论。
Mir-Ismaili

39

我用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)

18

我们最终使用类组件停止运行,并使用useEffect()Hooks 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;

2
对于使用React 16.8 Hooks update或更高版本的人,我认为切换到函数是一个理想的解决方案。在16.8中,函数可以访问状态和生命周期挂钩。
蒂姆(Tim)

5
我很困惑为什么这已经被否决了。React非常清楚地表明,类已被Hooks替换为功能组件。 reactjs.org/docs/...
马特·韦伯

3
我没有拒绝投票,但是在使用基于函数的组件时使用xhr设置惰性状态是一种痛苦。使用类组件,我可以将初始状态设置为所需的值,然后使用ajax然后在响应到达时使用setState。我完全不知道如何通过函数很好地做到这一点。
mlt

1
您将使用useEffect。在上述情况下,您将userName的初始状态设置为空字符串,然后在完成API调用后确保useEffect可以使用setUserName(response)。我将在上面添加一个示例和一个链接,以获取有关useEffect生命周期方法使用的更多信息。 dev.to/prototyp/...
马特·韦伯

3
由于功能编程吸引了需要体系结构的实际应用程序,因此这一点被否决了。它增强了JS程序员已经形成的大量意粉代码的趋势,这些意粉确实非常,非常难于阅读/关注并且无法分解为合理的组件。如果反应正在以这种方式推动他们犯了一个大错误,那么我不会在那儿跟随他们。
RickyA

2

useStyles 是一个React钩子,旨在用于功能组件,不能在类组件中使用。

从React:

挂钩使您无需编写类即可使用状态和其他React功能。

同样,您应该在函数内部调用useStyleshook,例如;

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↓上

编辑React钩子


0

另一个解决方案可用于类组件-只需使用MuiThemeProvider覆盖默认的MUI Theme属性即可。与其他方法相比,这将提供更大的灵活性-您可以在父组件中使用多个MuiThemeProvider。

简单的步骤:

  1. 将MuiThemeProvider导入您的类组件
  2. 将createMuiTheme导入您的类组件
  3. 创建新主题
  4. 包装要使用MuiThemeProvider和自定义主题设置样式的目标MUI组件

请检查此文档以获取更多详细信息: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;


-1

除了将类转换为函数外,一个简单的步骤是为包含使用“类”(如果您使用)的组件的jsx创建一个函数<container></container>,然后在类render()的返回内部调用此函数。作为标签。这样,您就可以从类中移出函数。它对我来说很完美。在我的情况下,这是<table>我将其移至函数TableStmt外部并在render内部将该函数称为<TableStmt/>

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.