Material ui class based components - reactjs

Where can I find material ui class based components instead of function based components and if I can,t.
Where to write
const classes = useClasses();
const theme = useTheme();
in my class component?

useTheme - Its a hook, which can be used with functional components
You can however use withTheme for class component.
Refer this link : https://material-ui.com/styles/advanced/

There are two HoCs for your use-case, i.e. withStyles and withTheme.
Usage:
import React from "react"
import { withStyles } from "#material-ui/core/styles"
const styles = theme => ({
wrapper: {
display: "flex",
// ...
}
// ...
})
class ClassComponent extends React.Component {
// ...
render() {
const { classes } = this.props
// ...
return (
<div className={classes.wrapper}>
{/* ... */}
</div>
)
}
}
export const EnhancedComponent = withStyles(styles)(ClassComponent)
Now you can render EnhancedComponent.

Related

Styling Class Components with an alternative to makeStyles

I am seeing examples of using the makeStyles hook so you can style your functional component in Material Design. But I am using a class component and so can't use hooks. The code I see being used for functional components is as follows:
const useStyles = makeStyles((theme) => ({
margin: {
margin: theme.spacing(1),
},
}));
And then for styling the elements in the return() section, they do something like this:
className={classes.margin}
How do I do the same type of thing but for a class component?
for class component you can use withStyles wrapper.
import React, { Component } from "react";
import { withStyles } from "#material-ui/core/styles";
class App extends Component {
render() {
const { classes } = this.props;
return <div className={classes.styledLine}>Styling using withStyles</div>;
}
}
const useStyles = (theme) => ({
styledLine: {
color: "red"
}
});
export default withStyles(useStyles)(App);
Working demo:-

Material UI: Use theme in React Class Component

I am looking for something like ThemeConsumer (which probably doesn't exist). I've React component and I am using withStyles() higher-order component to inject custom styles. It's pretty well described in documentation but I didn't find any example which uses theme.
I have some base component which contains ThemeProvider. It means any of MUI components are being affected by it.
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const theme = getTheme(prefersDarkMode);
return (
<ThemeProvider theme={theme}>
...
</ThemeProvider>
)
I also use some functional components with makeStyles() to create styles with provided theme.
const useStyles = makeStyles(theme => ({
// here I can use theme provided by ThemeProvider
});
But it can't be used in class components. So I am using withStyles() HOC.
const styles = {
// I would like to use here provided theme too
}
export default withStyles(styles)(SomeComponent);
Summary of my question:
How do I use provided theme in class component?
withStyles supports similar syntax as makeStyles:
const styles = theme => ({
// here I can use theme provided by ThemeProvider
});
export default withStyles(styles)(SomeComponent);
Here's a simple working example:
import React from "react";
import { withStyles } from "#material-ui/core/styles";
import Paper from "#material-ui/core/Paper";
const StyledPaper = withStyles(theme => ({
root: {
backgroundColor: theme.palette.secondary.main
}
}))(Paper);
export default function App() {
return (
<StyledPaper className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</StyledPaper>
);
}
A component decorated with withStyles(styles) gets a special classes prop injected, which may be used for your custom styles. For example:
import { Box } from "#material-ui/core"
import { withStyles } from "#material-ui/core/styles"
const styles = theme => ({
myCustomClass: {
color: theme.palette.tertiary.dark
}
})
class myComponent extends Component {
render() {
const { classes, theme } = this.props
// In last line, you see we have passed `{ withTheme: true }` option
// to have access to the theme variable inside the class body. See it
// in action in next line.
return <Box className={classes.myCustomClass} padding={theme.spacing(4)} />
}
}
export default withStyles(styles, { withTheme: true })(myComponent)
If you pass { withTheme: true } option to withStyles HOC, you'll get the theme variable injected as a prop also.
If you have other HOCs (e.g. Redux's connect, Router, etc) applied to your component, you may use it like this:
export default withStyles(styles, { withTheme: true })(
withRouter(connect(mapStateToProps)(myComponent))
)
A more comprehensive explanation for this topic could be found in this article: Using Material UI theme variable in React Function and Class Components.
Use withTheme HOC
modified example from docs
import { withTheme } from '#material-ui/core/styles';
class DeepChildRaw
{
/*...*/
render()
{
return <span>{`spacing ${this.props.theme.spacing}`}</span>;
}
}
const DeepChild = withTheme(DeepChildRaw);
if are working with Class Components, you can use like here ;)
import React from 'react';
import Routes from './Routes';
import '../custom.css';
import { BrowserRouter } from 'react-router-dom';
import { MuiThemeProvider, createTheme } from '#material-ui/core/styles';
const theme = createTheme({
palette: {
primary: {
main: '#fff'
},
secondary: {
main: '#351436'
}
}
});
class App extends React.Component {
render() {
return (
<div className="App">
<MuiThemeProvider theme={theme}>
<BrowserRouter>
<Routes />
</BrowserRouter>
</MuiThemeProvider>
</div>
)
}
}
export default App;

Ag-grid custom tooltip with functional component

I am looking at ag-Grid's example on creating a custom tooltip.
import React, {Component} from 'react';
export default class CustomTooltip extends Component {
getReactContainerClasses() {
return ['custom-tooltip'];
}
render() {
const data = this.props.api.getDisplayedRowAtIndex(this.props.rowIndex).data;
return (
<div className="custom-tooltip" style={{backgroundColor: this.props.color || 'white'}}>
<p><span>{data.athlete}</span></p>
<p><span>Country: </span> {data.country}</p>
<p><span>Total: </span> {data.total}</p>
</div>
);
}
}
According to ag-Grid's react component page, "If you wish to override the style of this div you can either provide an implementation of the ag-react-container class, or via the getReactContainerStyle or getReactContainerClasses callbacks on the React component:"
How would I go about creating a custom tooltip using a functional component? I am not sure how I would provide an implementation of the getReactContainerClasses callback.
You won't be able to have the public function getReactContainerClasses in a functional component, you'd need to write a class component. If you want to write a functional component, just set the CSS class directly on the container DOM element, similarly to their vanilla JS example. Below is a functional tooltip example which sets the class custom-tooltip.
import React, {Component} from 'react';
export const FunctionalCustomTooltip = (props) => {
props.reactContainer.classList.add('custom-tooltip');
const data = props.api.getDisplayedRowAtIndex(props.rowIndex).data;
return (
<div className="custom-tooltip" style={{backgroundColor: props.color || 'white'}}>
<p><span>{data.athlete}</span></p>
<p><span>Country: </span> {data.country}</p>
<p><span>Total: </span> {data.total}</p>
</div>
);
};
Fully working example:
https://plnkr.co/edit/WHEgtw0YVia1BVP4SVO8?p=preview
You can have public function using React Hooks with useImperativeHandle hook.
export const Component = forwardRef((props: ComponentParams, ref: any) => {
useImperativeHandle(ref, () => {
return {
getReactContainerClasses() {
return ['grid-container'];
},
};
});
}

React passing additional classNames to child component in addition to other props

I'd like to pass additional classNames into a child component, and also pass down any other props.
For example:
class Parent extends Component {
render() {
<Child
className="parent-class"
flavor="chocolate"
/>
}
}
class Child extends Component {
render() {
<div className="child-class" {...props}>
</div>
}
}
In this case, I would like the Child component div to have the "flavor" prop, and also have both classes "parent-class" and "child-class". However as it is, the className="child-class" will be overwritten by {...props}.
The only workaround I can think of is putting the {...props} before the className in the Child component:
<div {...props} className={`child-class ${props.className}`}>
Is this the only workaround? Or is there a cleaner solution?
I typically use the classnames package and the rest operator for things like this.
import classNames from 'classnames';
class Parent extends Component {
render() {
<Child
className="parent-class"
flavor="chocolate"
/>
}
}
class Child extends Component {
render() {
const { className, ...rest } = this.props;
const childClassNames = classNames('child-class', className);
return (
<div className={childClassNames} {...rest}>
</div>
);
}
}
You can call rest whatever you like, e.g. ...props will create an object variable called props which would contain everything from this.props except for className.
Also, the classnames package is very widely used, and lets you do other cool things like conditionally include class names.
If you are using functional components, the approach is almost identical to Bryan Downings answer. For simplicities sake I am only posting the implemented child component.
import { Tabs as AntdTabs } from 'antd'
/**
* simplified exmaple, based on our custom Antd Tabs*
* AntD Tabs with custom styling pre-applied.
* #param props The default TabsProps.
* #returns Antd Tabs.
*/
const Tabs = (props: TabsProps) => {
const {
className,
...rest
} = props
const cls = classnames(className, 'custom-tabs')
return (
<>
<AntdTabs
className={cls}
{...rest} >
</AntdTabs>
</>
)
}
export { Tabs }

How to pass Styled-Component theme variables to Components?

Within my React+StyledComponent app, I have a theme file like so:
theme.js:
const colors = {
blacks: [
'#14161B',
'#2E2E34',
'#3E3E43',
],
};
const theme = {
colors,
};
export default theme;
Currently, I can easily use these colors to style my components like so:
const MyStyledContainer = styled.div`
background-color: ${(props) => props.theme.colors.blacks[1]};
`;
The problem is, how do I pass blacks[1] to a Component as the prop of the color to use like so:
<Text color="black[1]">Hello</Text>
Where Text.js is:
const StyledSpan = styled.span`
color: ${(props) => props.theme.colors[props.color]};
`;
const Text = ({
color,
}) => {
return (
<StyledSpan
color={color}
>
{text}
</StyledSpan>
);
};
Text.propTypes = {
color: PropTypes.string,
};
export default Text;
Currently the above is silently failing and rending the following in the DOM:
<span class="sc-brqgn" color="blacks[1]">Hello</span>
Any ideas on how I can get this to work? Thank you
EDIT: Updated to use styled-components withTheme HOC
New answer
You could wrap the component rendering <Text> in the higher order component (HOC) withTheme provided by styled-components. This enables you to use the theme given to the <ThemeProvider> directly in the React component.
Example (based on the styled-components docs):
import React from 'react'
import { withTheme } from 'styled-components'
import Text from './Text.js'
class MyComponent extends React.Component {
render() {
<Text color={this.props.theme.colors.blacks[1]} />;
}
}
export default withTheme(MyComponent)
Then you could do
const MyStyledContainer = styled.div`
background-color: ${(props) => props.color};
`;
Old answer
You could import the theme where you render and pass <Text color={theme.blacks[1]} />.
import theme from './theme.js'
...
<Text color={theme.colors.blacks[1]} />
Then you could do
const MyStyledContainer = styled.div`
background-color: ${(props) => props.color};
`;
You can use defaultProps
import PropTypes from 'prop-types'
MyStyledContainer.defaultProps = { theme }
App.js
App gets theme and passes color to Text
import React, { Component } from 'react'
import styled from 'styled-components'
const Text = styled.div`
color: ${props => props.color || 'inherit'}
`
class App extends Component {
render() {
const { theme } = this.props
return (
<Text color={theme.colors.black[1]} />
)
}
}
export default App
Root.js
Root component passes theme to entire application.
import React, { Component } from 'react'
import { ThemeProvider } from 'styled-components'
import theme from './theme'
import App from './App'
class Root extends Component {
render() {
return (
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
)
}
}
export default Root
If you're using functional components in React and v4.x and higher styled-components, you need to leverage useContext and styled-components' ThemeContext. Together, these allow you to use your theme settings inside of components that aren't styled-components.
import { useContext } from 'react'
import { ThemeContext } from 'styled-components'
export default function MyComponent() {
// place ThemeContext into a context that is scoped to just this component
const themeProps = useContext(ThemeContext)
return(
<>
{/* Example here is a wrapper component that needs sizing params */}
{/* We access the context and all of our theme props are attached to it */}
<Wrapper maxWidth={ themeProps.maxWidth }>
</Wrapper>
</>
)
}
Further reading in the styled-components docs: https://styled-components.com/docs/advanced#via-usecontext-react-hook

Resources