Jak stylizować komponenty za pomocą makeStyles i nadal mieć metody cyklu życia w Material UI?

123

Otrzymuję poniższy błąd za każdym razem, gdy próbuję użyć makeStyles()komponentu z metodami cyklu życia:

Nieprawidłowe połączenie z podniesioną słuchawką. Hooki można wywoływać tylko wewnątrz treści składnika funkcji. Może się to zdarzyć z jednego z następujących powodów:

  1. Możesz mieć niezgodne wersje Reacta i renderera (np. React DOM)
  2. Być może łamiesz Zasady korzystania z hooków
  3. Możesz mieć więcej niż jedną kopię Reacta w tej samej aplikacji

Poniżej znajduje się mały przykład kodu, który powoduje ten błąd. Inne przykłady przypisują klasy również elementom podrzędnym. W dokumentacji MUI nie mogę znaleźć niczego, co wskazywałoby na inne sposoby użycia makeStylesi możliwość korzystania z metod cyklu życia.

    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;
Matt Weber
źródło

Odpowiedzi:

179

Cześć, zamiast korzystać z API hooka, powinieneś użyć API komponentu wyższego rzędu, jak wspomniano tutaj

Zmodyfikuję przykład w dokumentacji, aby odpowiadał Twoim potrzebom dotyczącym komponentu klasowego

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);
Vikas Kumar
źródło
4
Kręciłem się w kółko z tym błędem i invalid hook callbłędem - Dzięki za poprowadzenie mnie we właściwym kierunku !!
Kitson,
1
@ Jax-p zobacz moje rozwiązanie
Matt Weber
4
@VikasKumar Przy takim podejściu, jak mogę używać motywu aplikacji w moich stylach? Prześlij proszę: {margin: appTheme.spacing (3, 0, 2),},
Sergey Aldoukhov
1
Dzięki. Ale problem! Nie używałeś themew swoim stylesciele (@SergeyAldoukhov już to powiedział). Kiedy go używam, pojawia się ten błąd: „Nie można odczytać właściwości 'X' o wartości undefined” i undefinedjest themedokładnie! Próbowałem withStyles(styles(myDefinedMuiTheme))(...)i zadziałało poprawnie.
Mir-Ismaili
1
@Kitson, prawdopodobnie użyłeś makeStyles() ( styles = makeStyles(theme => ({...})) . Ponadto, jeśli chcesz mieć styl zależny od motywu, zobacz mój poprzedni komentarz.
Mir-Ismaili
50

Użyłem withStyleszamiastmakeStyle

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)
Hamed
źródło
21

Skończyło się na tym, że przestaliśmy używać komponentów klas i utworzyliśmy komponenty funkcjonalne, używającuseEffect() z Hooks API do metod cyklu życia . Dzięki temu można nadal korzystać makeStyles()z metod cyklu życia bez dodawania komplikacji związanych z tworzeniem komponentów wyższego rzędu . Co jest znacznie prostsze.

Przykład:

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;
Matt Weber
źródło
2
Dla osób korzystających z aktualizacji Hooks w React 16.8 lub nowszej, myślę, że przejście na funkcję jest idealnym rozwiązaniem. W 16.8 funkcje mogą uzyskać dostęp do haków stanu i cyklu życia.
Tim
5
Jestem zdumiony, dlaczego otrzymałem głosy negatywne. React wyjaśnił, że klasy są zastępowane komponentami funkcjonalnymi z hookami. actjs.org/docs/…
Matt Weber
3
Nie głosowałem przeciw, ale uciążliwe jest ustawienie stanu początkowego w leniwy sposób za pomocą xhr podczas korzystania z komponentu opartego na funkcjach. Z komponentem klasy mogę ustawić stan początkowy na cokolwiek chcę, a następnie użyć ajax, a następnie setState po nadejściu odpowiedzi. Całkowicie nie mam pojęcia, jak ładnie to zrobić za pomocą funkcji.
mlt
1
Używałbyś useEffect. W powyższym przypadku ustawiasz początkowy stan userName na pusty ciąg, a następnie po wykonaniu wywołania API upewnij się useEffect, że użyjesz setUserName(response). Dodam powyższy przykład i link do artykułu zawierającego więcej informacji na temat zastosowania metody useEffect w metodach cyklu życia. dev.to/prototyp/…
Matt Weber,
3
Jest to głosowanie w dół, ponieważ programowanie funkcjonalne zasysa rzeczywiste aplikacje, które wymagają architektury. Wzmacnia i tak już rozpowszechnioną tendencję programistów js do tworzenia dużych bzdur kodu spaghetti, który jest naprawdę, bardzo trudny do odczytania / śledzenia i niemożliwy do podzielenia na rozsądne komponenty. Jeśli tak reagują, popełniają duży błąd i nie będę ich tam podążał.
RickyA
2

useStyles to hak React, który ma być używany w komponentach funkcjonalnych i nie może być używany w komponentach klas.

Z Reacta:

Hooki pozwalają ci używać stanu i innych funkcji Reacta bez pisania klasy.

Powinieneś także wywołać useStyleshook wewnątrz swojej funkcji, np.;

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

Jeśli chcesz używać hooków, oto twój krótki komponent klasy zamieniony na komponent funkcjonalny;

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;

🏓 on ↓ CodeSandBox ↓

Edytuj hooki Reacta

Hasan Sefa Ozalp
źródło
0

Inne rozwiązanie można zastosować dla komponentów klas - wystarczy zastąpić domyślne właściwości motywu MUI za pomocą MuiThemeProvider. Zapewni to większą elastyczność w porównaniu z innymi metodami - możesz użyć więcej niż jednego MuiThemeProvider wewnątrz komponentu nadrzędnego.

proste kroki:

  1. zaimportuj MuiThemeProvider do komponentu klasy
  2. importuj createMuiTheme do komponentu klasy
  3. utwórz nowy motyw
  4. zawiń docelowy komponent MUI, który chcesz stylizować, za pomocą MuiThemeProvider i niestandardowego motywu

proszę sprawdzić ten dokument, aby uzyskać więcej informacji: 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;

viktorma
źródło
-1

Zamiast konwertować klasę na funkcję, prostym krokiem byłoby utworzenie funkcji zawierającej jsx dla komponentu, który używa `` klas '', w twoim przypadku, <container></container>a następnie wywołaj tę funkcję wewnątrz zwracanej klasy render () jako tag. W ten sposób przenosisz zaczep do funkcji z klasy. U mnie zadziałało idealnie. W moim przypadku był to element, <table>który przeniosłem do funkcji TableStmt na zewnątrz i nazwałem tę funkcję wewnątrz renderowania jako<TableStmt/>

jayesh
źródło