Material-UI. Invalid hook call - reactjs

I've been following some examples on how to setup Material-UI with React.
Whatever I try, I'm getting back the following error:
Error: Minified React error #321; visit
https://reactjs.org/docs/error-decoder.html?invariant=321 for the full
message or use the non-minified dev environment for full errors and
additional helpful warnings.
This seems to be Hook related, but I can't find any reason why this happens.
The component
Button
import React from 'react';
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
const styles = {
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 ButtonComp(props) {
const { classes } = props;
return <Button className={classes.root}>Higher-order component</Button>;
}
export default withStyles(styles)(ButtonComp);
Implemented like this
import * as React from 'react';
import ButtonComp from './ButtonComp';
const App = ({ children }) => {
return (
<React.Fragment>
<ButtonComp />
<main>
{children}
</main>
</React.Fragment>
);
};
export default (App);
I'm running on react#16.11.0 and #material-ui/core#4.5.1.
What am I missing here? I can't find the problem at all.

Related

MUI styled cannot pass component prop to Typography in typescript

Using MUI with typescript and want to use styled from MUI as well. When passing the component prop to the styled component I get an error from the typescript sandbox below, maybe anyone knows a workaround.
https://codesandbox.io/s/material-demo-forked-lxdrj?file=/demo.tsx
You have to manually add the 'component' prop.
From https://mui.com/material-ui/guides/typescript/#complications-with-the-component-prop
import React from "react";
import type { TypographyProps } from "#material-ui/core";
import { styled } from "#material-ui/core/styles";
import Typography from "#material-ui/core/Typography";
type MyT = React.ComponentType<TypographyProps<"span", { component: "span" }>>;
const MyTypography: MyT = styled(Typography)({
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"
});
export default function StyledComponents() {
return (
<MyTypography component="span">Styled with styled-components API</MyTypography>
);
}
You can use generic type parameter in HOC created by styled:
import { styled } from '#mui/material/styles';
import Typography from '#mui/material/Typography';
type ExtraProps = {
component: React.ElementType;
};
const MyTypography = styled(Typography)<ExtraProps>({
// ...
});
Live Demo
Same idea as #GitGitBoom, but without having to define a type. You can use the solution described in the MUI Typescript Guide where you just cast your styled component to the typeof Typography
import React from "react";
import { styled } from "#material-ui/core/styles";
import Typography from "#material-ui/core/Typography";
const MyTypography = styled(Typography)({
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"
}) as typeof Typography;
export default function StyledComponents() {
return (
<MyTypography component="span">
Styled with styled-components API
</MyTypography>
);
}

Missing styles when using React with Web Components

I'm creating a widget module that will then be assembled into a bundle and connected to the static sites in a single file. One of the main problems is applying the styles of the parent sites to the widget module. Because of this, an attempt was made to encapsulate the widget using the shadow dom.
import React from 'react';
import ReactDOM from 'react-dom';
import styled from 'styled-components';
const StyledButton = styled.button`...`;
function ModuleComponent() {
return (
<div>
<StyledButton>I does not work</StyledButton>
<button style={{ height: 40, color: "red" }}>It works</button>
</div>
);
}
class IncapsulatedReactWidgetElement extends HTMLElement {
connectedCallback() {
const mountPoint = document.createElement("div");
this.attachShadow({ mode: "open" }).appendChild(mountPoint);
ReactDOM.render(<ModuleComponent />, mountPoint);
}
}
customElements.define(
"incapsulated-react-widget-element",
IncapsulatedReactWidgetElement
);
function Root() {
return <incapsulated-react-widget-element />;
}
const MOUNT_NODE = document.getElementById("root");
ReactDOM.render(<Root />, MOUNT_NODE);
Actually it works as expected except the styles. I'm trying to use styled components but nothing works expect inline styles. As you can see, I do two render actions: first for integration ModuleComponent into custom element and second - for render the app. In which way styles can get lost?
Correct way for applying styles is using Style Hook for React. Make use of makeStyles and useStyles and its an standard way of doing it.
This may help for quick start: https://material-ui.com/styles/basics/#hook-api
The inline styles that you have used are also kinda a subset of it.
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
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',
},
});
export default function Hook() {
const classes = useStyles();
return <Button className={classes.root}>Hook</Button>;
}
if you are going to have larger stylesheets, then we can separate it to different file. For site wide changes, you can use createStyle hook.

Hooks can only be called inside of the body of a function component

I created a react js project with Material UI
And then I was building and publishing the whole project with
build": "./node_modules/.bin/babel src --out-file BuildTest/index.js
When I installed this module in Client-side(Other Project) I got this problem:
Module :
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
const styles = {
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 HigherOrderComponent(props) {
const { classes } = props;
return <Button className={classes.root}>Higher-order component</Button>;
}
HigherOrderComponent.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(HigherOrderComponent);
Client-Side:
import Mtest from "testmodule";
import React, { Component } from "react";
export default class App extends Component {
render() {
return (
<div>
<Mtest/>
</div>
)
}
}

material ui styles inside react component

Here you can see example of using material ui styles outside of a react component.
import React from 'react';
import { makeStyles } from '#material-ui/styles';
import Button from '#material-ui/core/Button';
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',
},
});
export default function Hook() {
const classes = useStyles();
return <Button className={classes.root}>Hook</Button>;
}
I want to do the same, but inside react component:
class Component extends React.Component {
render() {
const {height} = this.props
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: height,
padding: '0 30px',
},
});
const classes = useStyles();
return <Button className={classes.root}>Hook</Button>;
}}
Is it possible?
I see that here have to be correct version of react with hooks , but I've not found any example where peoples use it inside class makeStyles
makeStyles creates a React hook; because of this, you cannot inter-operate with class components.
Hooks are used to replace classes entirely since React is moving more in the direction of purely functional components for the sake of compiler optimizations and other low level things (though there are external benefits for devs as well such as being less verbose and taking better advantage of and being more intuitive with the functional nature of JS). In the case of makeStyles, you get additional benefits such as being able to use any variable including those from what would traditionally be props and state, and automatically your JSS re-calculates only based on observable params you have provided vs when any prop changes.
Instead, as #Richard Ansell pointed out, you should use withStyles if you are not comfortable outside classes. This is a High Order Component. Would suggest though that in newer code bases, you learn hooks and adapt to that as hooks can represent almost all functionality from both HOCs and components when you become better at them.
Below is the example given from the material-ui documentation (RTFM here):
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/styles';
import Button from '#material-ui/core/Button';
const styles = {
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 HigherOrderComponent(props) {
const { classes } = props;
return <Button className={classes.root}>Higher-order component</Button>;
}
HigherOrderComponent.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(HigherOrderComponent);
From what I can gather, you can simply use the withStyles API that can wrap your component and inject your style directly into your exported component. See the following page that will explain this in more detail: withStyles
Example:
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/styles';
import Button from '#material-ui/core/Button';
const styles = {
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',
padding: '0 30px',
},
};
function HigherOrderComponent(props) {
const { classes, customHeight } = props;
return <Button className={classes.root} style={{minHeight: customHeight, maxHeight: customHeight}}>Higher-order
component</Button>;
}
HigherOrderComponent.propTypes = {
classes: PropTypes.object.isRequired,
customHeight: PropTypes.object.isRequired
};
export default withStyles(styles)(HigherOrderComponent);

How do I override compiled classes?

I cam trying to apply override styles to a compiled class name. my compiled code is like...
<div class="MuiListItemText-root-262" >
I want to be able to target that specific item like this
const styles = () => { MultiListItemText-root-262: { color: red; } }
in vanilla CSS I could just do .MultiListItemText-root-262: { color: red; }
How can I do the equivalent in JSS?
You cannot do it this way.
The classname "MuiListItemText-root-262" is dynamic, and the id "262" is not reliable and may change.
Please look at the official documentation of Material UI for using JSS overrides : https://material-ui.com/customization/overrides/
There are several techniques available depending on the level of variation your want to achieve.
For a typical "one time" override, see the first sample code which uses the withStyles HOC
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
// We can inject some CSS into the DOM.
const styles = {
button: {
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
borderRadius: 3,
border: 0,
color: 'white',
height: 48,
padding: '0 30px',
boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
},
};
function ClassNames(props) {
return (
<Button className={props.classes.button}>
{props.children ? props.children : 'class names'}
</Button>
);
}
ClassNames.propTypes = {
children: PropTypes.node,
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(ClassNames);

Resources