React Material UI Syntax with Props Explained - reactjs

I understand the basic of react code such as passing data to child component and passing data back to parent with props. I encountered this code below from React Material UI which is abit confusing. Was hoping to get some explanation.
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import CircularProgress from '#material-ui/core/CircularProgress';
import purple from '#material-ui/core/colors/purple';
const styles = theme => ({
progress: {
margin: theme.spacing.unit * 2,
},
});
function CircularIndeterminate(props) {
const { classes } = props;
return (
<div>
<CircularProgress className={classes.progress} />
<CircularProgress className={classes.progress} size={50} />
<CircularProgress className={classes.progress} color="secondary" />
<CircularProgress className={classes.progress} style={{ color: purple[500] }} thickness={7} />
</div>
);
}
CircularIndeterminate.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(CircularIndeterminate);
Here is what I understand.
1.The component takes in props so I can pass data down to this child component and before render it will extract the classes property from props.classes.
2.There is a styles function that takes in a theme variable and returns an object progress which has some styling which is used by classes.progress
3.withStyle is some sort of higher order component.
But with this knowledge, this code is abit confusing.
How come the code can do classes.progress, why does classes variable have anything to do with the progress style
What is the purpose of
CircularIndeterminate.propTypes = {
classes: PropTypes.object.isRequired,
};

classes variable that is that is passed in as a prop is an object with progress being one of its key. The classes object might look like
classes = {
progress: 'half'
}
Now the value of classes.progress which in the above classes object is half is assigned as a className prop to the CircularProgress component.
Regarding
CircularIndeterminate.propTypes = {
classes: PropTypes.object.isRequired,
};
The above syntax is to add typechecking capabilities to your component. React has some built-in typechecking abilities. To run typechecking on the props for a component, you can assign the special propTypes property.
In your case, it means that the CircularIndeterminate expects a prop classes which is of type object and is a Required prop.
To read more about PropTypes, please visit the docs

Related

How to use sx prop in styled components without mui

Instead of using multiple props for each css attribute, I want to make an sx prop like material-ui has. I'm trying to implement this with styled-components. The idea is that I can pass an object as a prop, and reference a value from the object in the styled component.
things to note:
material-ui is not added as a dependancy. I want to "clone" sx from it.
currently looking into prop-types to add some value in finding bug.
Button Component
import styled from "styled-components";
export const Button = styled.button`
background-color: ${(sx) => sx.backgroundColor};
color: ${(sx) => sx.color};
`;
Rendering Button
import React from "react";
import styled from "styled-components";
import { Button } from "./Button/Button";
function App() {
return (
<>
<Button sx={{ backgroundColor: "red", color: "white" }} />
<>
);
}
export default App;
Update:
After adding prop-types to maybe find the issue, I get an error when I pass the prop to the Button component when I added this code:
// typechecking prop types passed
Button.propTypes = {
sx: PropTypes.string,
};
And the error I got back:
Warning: Failed prop type: Invalid prop `sx` of type `object` supplied to `styled.button`, expected `string`
Im not sure exactly where its going wrong. I've tried doing a console.log to see the value passed, which seems to be a string. Therefore dot notation should work?
Any help would be appreciated, thanks very much :)
After adding prop-types and finding the passed types, I was able to find this answer:
// required prop to be passed as an object
Button.propTypes = {
sx: PropTypes.object,
};
// updating the props for Button component
export const Button = styled.button`
background-color: ${(props) => props.sx.backgroundColor};
color: ${(props) => props.sx.color};
`;
then using sx as normal...
import React from "react";
import styled from "styled-components";
import { Button } from "./Button/Button";
function App() {
return (
<>
<Button sx={{ backgroundColor: "red", color: "white" }} />
</>
);
}
export default App;

Material UI nested theme providers breaks withStyles HOC

I have a React application created with Create React App and I use the #material-ui/core npm package for theming.
To customize components I use the withStyles higher-order component provided by MaterialUI.
According to documentation it supports nested ThemeProviders https://material-ui.com/customization/theming/#nesting-the-theme.
But inside the child ThemeProvider withStyles won't apply classes.
Here is a basic application demonstrating the issue -> https://codesandbox.io/s/vibrant-tree-eh83d
ExampleComponent.tsx
import React, { FunctionComponent } from "react";
import {
WithStyles,
withStyles,
createStyles,
StepButton,
Step,
Stepper,
Box
} from "#material-ui/core";
const styles = createStyles({
button: {
"& .MuiStepIcon-root.MuiStepIcon-active": {
fill: "red"
}
}
});
interface Props extends WithStyles<typeof styles> {
title: string;
}
const ExampleComponent: FunctionComponent<Props> = ({ title, classes }) => {
console.log(title, classes);
return (
<Box display="flex" alignItems="center">
<span>{title}</span>
<Stepper activeStep={0}>
<Step>
<StepButton className={classes.button}>Test</StepButton>;
</Step>
</Stepper>
</Box>
);
};
export default withStyles(styles)(ExampleComponent);
App.tsx
import * as React from "react";
import { ThemeProvider, createMuiTheme } from "#material-ui/core";
import ExampleComponent from "./ExampleComponent";
const theme = createMuiTheme();
function App() {
return (
<ThemeProvider theme={theme}>
<ExampleComponent title="Root" />
<ThemeProvider theme={theme}>
<ExampleComponent title="Nested" />
</ThemeProvider>
</ThemeProvider>
);
}
export default App;
Inside the ExampleComponent I console.log the generated classes object.
I want to use nested ThemeProviders and override classes inside components regardless of the ThemeProvider.
Am I missing something or is this not possible?
When you are using nested themes, you cannot reliably use Material-UI's global class names (e.g. .MuiStepIcon-root.MuiStepIcon-active). Within a nested theme, the "Mui..." class names have to be different to avoid conflicting with the CSS classes for the top-level theme since the nested theme will cause some of the CSS for the "Mui..." classes to be different.
You can use the following syntax in order to successfully match the suffixed versions of the Mui class names that occur within nested themes:
const styles = createStyles({
button: {
'& [class*="MuiStepIcon-root"][class*="MuiStepIcon-active"]': {
fill: "red"
}
}
});
Related answer:
How reliable are MUI Global Class names in JSS?

Locate a component in React project

I wrote a Logout button component in my React app for which I wish to locate at the top right corner of the screen.
render() {
<LogoutButtonComponent height: , backgroudColor: />
}
It wouldn't let me assign any values for height and etc.
This is the Logout component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class LogOutButton extends Component {
static contextTypes = {
store: PropTypes.object.isRequired,
};
handleClick = () => {
this.props.onLogout();
};
render() {
return <button type="button" onClick={this.handleClick}>Logout</button>;
}
}
Should I locate it by < col /> ?
To add inline styles, you should defined a style object as prop, and pass it values, like doniyor2109 mentioned. There are a few caveats to using this, however.
style={{ height: 100, height: '100px', height: '100%', minHeight: '100px'}}.
Not every value should be passed as integer, some need to be passed as a string
Not every css attribute gets passed as you would expect them to, the css min-height actually gets passed as minHeight, so replace all hyphens with lower camel case style
Inline styles get insanely difficult to manage. I suggest you at the very least create an object outside the component, and pass it in like:
const DivStyle = { minHeight: '100px' }
and then:
<LogoutButtonComponent style={DivStyle} />
You can prefix that DivStyle with an export if you want to import {DivStyle} from './somefile' in other places
I suggest you check out a library like styled-components as it makes styling much easier!
I suggest you check out this article which outlines your options
You don't really add styles to your component like that. It's better to add those styles in the source for the actual component. So how exactly do you want it displayed? I will provide a template kind of and you can change it to what you want.
Go to your source for your Logout Button Component. In the return of your render method try adding a div call it container. Then add styling in a css file to that div or if you are using react-bootstrap or reactstrap or #material/ui/core you can adjust the style according to their documentation.
You can add your css for the className .container to make it appear the way you would like.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class LogOutButton extends Component {
static contextTypes = {
store: PropTypes.object.isRequired,
};
handleClick = () => {
this.props.onLogout();
};
render() {
return (
<div className="container">
{* notice the className here *}
<button type="button" onClick={this.handleClick}>Logout</button>
</div>
)
}
}
Hope this helps.

How to extend prop types of Material UI components using Flow when passing through props?

I'd like to add Flow Types to an existing React project which uses Material UI.
I have a number of components which render a Material UI component and pass through any props received. For example:
import React from "react";
import IconButton from "#material-ui/core/IconButton";
import backIcon from "assets/icons/Back.svg";
const BackButton = ({ height, ...props }) => (
<IconButton {...props}>
<img
src={backIcon}
alt="back"
style={{
height,
width: height
}}
/>
</IconButton>
);
BackButton.defaultProps = {
height: "1.5rem"
};
export default BackButton;
If I were to define prop types for this, I figure it would look something like
type BackButtonProps = IconButtonProps & {
height: string
}
const BackButton = ({height, ...props}): BackButtonProps => (
...
)
My issue is that I'm not sure where to get IconButtonProps from. I've run flow-typed install and have a flow-typed/npm/#material-ui folder, but I can't figure out how to import IconButtonProps. I've tried the following:
import type { IconButtonProps } from "#material-ui/core/IconButton"
But the import is unresolved.
Where can I import the prop types of Material UI components from, in order to be able to extend them?

How to compose Material UI components with React and Typescript?

I would like to create an extension of a component from the Material UI library. For example, I am always adding styles to the wrapper in a modal, so I would like to create a custom component encapsulating those styles.
I've found the following solution that "works" but I feel like the props interface is a bit of a hack. Is there a better way to do this? Specifically, what is the best way to merge the classes prop from the original <Modal> component with the classes from my custom styles object?
import { Modal, WithStyles, createStyles, withStyles } from '#material-ui/core';
import { ModalClassKey, ModalProps } from '#material-ui/core/Modal';
import * as React from 'react';
interface IProps
extends WithStyles<typeof styles | ModalClassKey>,
Omit<ModalProps, 'classes'> {}
const styles = createStyles({
modal: {
// Some styles to apply to the modal content
},
});
const MyModal: React.SFC<IProps> = ({ children, classes, ...props }) => (
<Modal {...props}>
<div className={classes.modal}>
{children}
</div>
</Modal>
);
export default withStyles(styles)(MyModal);
You can use className on a material UI component.
I.E
<Typography className="large-text">
LORE IPSUM large
</Typography>

Resources