จะจัดสไตล์ส่วนประกอบโดยใช้ makeStyles และยังมีวิธี Lifecycle ใน Material UI ได้อย่างไร


123

ฉันได้รับข้อผิดพลาดด้านล่างเมื่อใดก็ตามที่ฉันพยายามใช้makeStyles()กับส่วนประกอบด้วยวิธีวงจรชีวิต:

สายเบ็ดไม่ถูกต้อง ตะขอสามารถเรียกได้เฉพาะภายในร่างกายของส่วนประกอบฟังก์ชันเท่านั้น สิ่งนี้อาจเกิดขึ้นจากสาเหตุใดสาเหตุหนึ่งต่อไปนี้:

  1. คุณอาจมีเวอร์ชันของ React และ renderer ที่ไม่ตรงกัน (เช่น React DOM)
  2. คุณอาจทำผิดกฎของ Hooks
  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;

คำตอบ:


179

สวัสดีแทนที่จะใช้ hook API คุณควรใช้ Higher-order component 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ข้อผิดพลาดนี้ - ขอบคุณที่พาฉันไปในทิศทางที่ถูกต้อง !!
Kitson

1
@ Jax-p ดูวิธีแก้ปัญหาของฉัน
Matt Weber

4
@VikasKumar ด้วยวิธีนี้ฉันจะใช้ธีมแอพในสไตล์ของฉันได้อย่างไร? Fe submit: {margin: appTheme.spacing (3, 0, 2),},
Sergey Aldoukhov

1
ขอบคุณ. แต่ปัญหา! คุณไม่ได้ใช้themeในstylesร่างกายของคุณ(@SergeyAldoukhov ได้กล่าวไว้แล้ว) เมื่อฉันใช้มันฉันได้รับข้อผิดพลาดนี้: "ไม่สามารถอ่านคุณสมบัติ 'X' ของไม่ได้กำหนด"และundefinedเป็นthemeที่แน่นอน! ฉันพยายามwithStyles(styles(myDefinedMuiTheme))(...)และมันทำงานได้อย่างถูกต้อง
Mir-Ismaili

1
@Kitson, คุณอาจได้ใช้makeStyles() (styles = makeStyles(theme => ({...}) ) นอกจากนี้หากคุณต้องการสไตล์ที่ขึ้นกับธีมโปรดดูความคิดเห็นก่อนหน้าของฉัน
Mir-Ismaili

50

ฉันใช้withStylesแทนmakeStyle

EX:

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)

21

สิ่งที่เราจบลงด้วยการทำคือการหยุดใช้ส่วนประกอบชั้นเรียนและสร้างส่วนประกอบของการทำงานโดยใช้useEffect()จากAPI ตะขอสำหรับวิธีวงจร สิ่งนี้ช่วยให้คุณยังคงใช้makeStyles()กับ Lifecycle Methods ได้โดยไม่ต้องเพิ่มความยุ่งยากในการสร้างส่วนประกอบที่มีลำดับสูง ซึ่งง่ายกว่ามาก

ตัวอย่าง:

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 ขึ้นไปฉันคิดว่าการเปลี่ยนไปใช้ฟังก์ชันเป็นทางออกที่ดี ในฟังก์ชัน 16.8 สามารถเข้าถึงตะขอสถานะและวงจรชีวิตได้
ทิม

5
ฉันงงว่าทำไมถึงได้รับการโหวตลดลง React ทำให้คลาสที่ค่อนข้างชัดเจนถูกแทนที่ด้วยส่วนประกอบที่ใช้งานได้ด้วย Hooks reactjs.org/docs/…
Matt Weber

3
ฉันไม่ได้ลงคะแนน แต่มันเป็นความเจ็บปวดที่ต้องตั้งค่าสถานะเริ่มต้นด้วยวิธีขี้เกียจโดยใช้ xhr ในขณะที่ใช้ส่วนประกอบตามฟังก์ชัน ด้วยองค์ประกอบคลาสฉันสามารถตั้งค่าสถานะเริ่มต้นเป็นสิ่งที่ฉันต้องการจากนั้นใช้ ajax จากนั้น setState เมื่อมีการตอบสนอง ฉันไม่รู้เลยว่าจะทำอย่างไรกับฟังก์ชั่นได้ดี
mlt

1
คุณจะใช้useEffect. ในกรณีดังกล่าวข้างต้นที่คุณกำลังตั้งสถานะเริ่มต้นของชื่อผู้ใช้เป็นสตริงว่างแล้วหลังจากการเรียก API ทำประกันของคุณจะใช้useEffect setUserName(response)ฉันจะเพิ่มตัวอย่างด้านบนและลิงก์ไปยังบทความที่มีข้อมูลเพิ่มเติมเกี่ยวกับการใช้ useEffect สำหรับวิธีวงจรชีวิต dev.to/prototyp/…
Matt Weber

3
สิ่งนี้ได้รับการโหวตลงเนื่องจากการเขียนโปรแกรมเชิงฟังก์ชันดูดในแอปพลิเคชันจริงที่ต้องการสถาปัตยกรรม ช่วยเพิ่มแนวโน้มที่เพิ่มขึ้นอย่างรวดเร็วของโปรแกรมเมอร์ js ในการสร้างรหัสสปาเก็ตตี้ขนาดใหญ่ที่อ่าน / ติดตามยากจริงๆและเป็นไปไม่ได้ที่จะแยกออกเป็นส่วนประกอบที่สมเหตุสมผล หากปฏิกิริยาผลักดันแบบนี้พวกเขากำลังทำผิดพลาดครั้งใหญ่และฉันจะไม่ติดตามพวกเขาที่นั่น
RickyA

2

useStyles เป็น React hook ซึ่งมีไว้เพื่อใช้ในส่วนประกอบที่ใช้งานได้และไม่สามารถใช้ในส่วนประกอบของคลาสได้

จาก React:

Hooks ช่วยให้คุณใช้ state และคุณสมบัติ React อื่น ๆ ได้โดยไม่ต้องเขียนคลาส

นอกจากนี้คุณควรเรียกuseStyleshook ภายในฟังก์ชันของคุณเช่น

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

หากคุณต้องการใช้ hook นี่คือส่วนประกอบของคลาสสั้น ๆ ของคุณที่เปลี่ยนเป็นส่วนประกอบการทำงาน

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 hooks


0

อีกวิธีหนึ่งที่สามารถใช้สำหรับส่วนประกอบของคลาส - เพียงแค่แทนที่คุณสมบัติเริ่มต้นของธีม 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;


-1

แทนที่จะแปลงคลาสเป็นฟังก์ชันขั้นตอนง่ายๆคือสร้างฟังก์ชันเพื่อรวม jsx สำหรับคอมโพเนนต์ที่ใช้ 'คลาส' ในกรณีของคุณ<container></container>จากนั้นเรียกใช้ฟังก์ชันนี้ภายในการส่งคืนคลาสเรนเดอร์ () เป็นแท็ก วิธีนี้คุณกำลังย้าย hook ไปยังฟังก์ชันจากคลาส มันทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน ในกรณีของฉันมันเป็นสิ่ง<table>ที่ฉันย้ายไปที่ฟังก์ชัน TableStmt ด้านนอกและเรียกฟังก์ชันนี้ภายในการเรนเดอร์ว่า<TableStmt/>

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.