For some reason my material ui styles are not applying to my html element? Any idea why? I have no other styles applied to this page
import React, { Component } from 'react';
import LoginForm from '../components/form/loginForm';
import { makeStyles } from '#material-ui/core';
const classes = makeStyles( (theme) => ({
root: {
paddingTop: theme.spacing(8),
backgroundColor: "white"
},
}) )
class Login extends Component {
render() {
return(
<div className = {classes.root}>
<LoginForm/>
</div>
);
}
}
export default Login;
In your case, if you want to style your class component, you should use withStyles. Try this:
import React, { Component } from 'react';
import LoginForm from '../components/form/loginForm';
import { withStyles } from '#material-ui/core/styles';
const useStyles = (theme) => ({
root: {
paddingTop: theme.spacing(8),
backgroundColor: "white"
},
})
class Login extends Component {
render() {
const { classes } = this.props
return(
<div className = {classes.root}>
<LoginForm/>
</div>
);
}
}
export default withStyles(useStyles)(Login);
makeStyles returns a react hook to use in the component. Hooks also only work in functional components, so you'll need to convert Login to a functional component.
import React, { Component } from 'react';
import LoginForm from '../components/form/loginForm';
import { makeStyles } from '#material-ui/core';
const useStyles = makeStyles(theme => ({
root: {
paddingTop: theme.spacing(8),
backgroundColor: "lightblue"
}
}));
const Login = props => {
const classes = useStyles();
return(
<div className={classes.root}>
<LoginForm/>
</div>
);
}
export default Login;
Related
I'm having an issue where I'm using the styled function to style a custom React component but the styles are not being applied. In the example below, I would expect the Child component to have the color: red style, but it doesn't. The sibling component, however, is styled correctly.
import "./styles.css";
import { Child } from "./Child";
import { Typography } from "#mui/material";
import { styled } from "#mui/material/styles";
const StyledChild = styled(Child)(() => ({
color: "red"
}));
const StyledSibling = styled(Typography)(() => ({
color: "blue"
}));
export default function App() {
return (
<>
<StyledChild />
<StyledSibling>Sibling</StyledSibling>
</>
);
}
import { Typography } from "#mui/material";
import { FunctionComponent } from "react";
export const Child: FunctionComponent = () => {
return <Typography>Child</Typography>;
};
CodeSandbox
styled causes a className prop to be passed to the wrapped component, but Child isn't passing the className prop along to Typography.
Here's an example of how to fix Child.tsx:
import { Typography } from "#mui/material";
import { FunctionComponent } from "react";
export const Child: FunctionComponent<{ className?: string }> = ({
className
}) => {
return <Typography className={className}>Child</Typography>;
};
How can I pass makeStyle classes from parent component to child component and combine them with the makeStyle classes in the child component? E.g. as below adding the breakpoint hiding to the child component style.
Example child component:
import React from "react"
import { Button } from "#material-ui/core"
import { makeStyles } from "#material-ui/core/styles"
const useStyles = makeStyles(theme => ({
button: {
background: "#000",
color: "white",
//lots of other css here so we dont want to repeat it in the parent component
},
}))
export default function PrimaryButton(props) {
const classes = useStyles()
const { children, fullWidth = false } = props
return (
<Button
fullWidth={fullWidth}
className={classes.button}
variant="contained"
>
{children}
</Button>
)
}
Example parent component:
import React from "react"
import { PrimaryButton } from "components/PrimaryButton"
import { makeStyles } from "#material-ui/core/styles"
const useStyles = makeStyles(theme => ({
primaryButton: {
display: "inline-block",
[theme.breakpoints.down("sm")]: {
display: "none",
},
},
}))
export default function PrimaryButton(props) {
const classes = useStyles()
return (
<PrimaryButton
className={classes.primaryButton}
>
Button text
</PrimaryButton>
)
}
clsx is used internally within Material-UI and is a convenient utility for combining multiple class names. In your child component, you can grab className from the props and then use className={clsx(className, classes.button)} in the Button it renders:
import React from "react";
import { Button } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
import clsx from "clsx";
const useStyles = makeStyles(theme => ({
button: {
background: "#000",
color: "white"
}
}));
export default function PrimaryButton(props) {
const classes = useStyles();
const { children, className, fullWidth = false } = props;
return (
<Button
fullWidth={fullWidth}
className={clsx(className, classes.button)}
variant="contained"
>
{children}
</Button>
);
}
I'm learning React with TypeScript and using the Material UI framework for the frontend. I try to get the media queries working, but I got an error:
Uncaught TypeError: Cannot read property 'up' of undefined
at styles (webpack-internal:///./app/components/navigation/Toolbar/index.tsx:59)
This is the corresponding code:
const styles = ({breakpoints}: Theme) => createStyles( {
grow: {
flexGrow: 1
},
menuButton: {
marginLeft: -12,
marginRight: 20
},
sectionDesktop: {
display: 'none',
[breakpoints.up('md')]: {
display: 'flex'
}
},
sectionMobile: {
display: 'flex'
},
})
The styles are passed to the component with:
export default withStyles(styles)(Toolbar)
I read that it is not required to create a custom theme as the default one will be passed automatically to the functions. However, the breakpoints property of theme is undefined which cause a blank page.
Thanks for your help
Edit
Here is the code of the component which will still produce the problem without any other components.
import * as React from 'react'
import {
Theme
} from '#material-ui/core'
import {
createStyles,
WithStyles,
withStyles
} from '#material-ui/styles'
// import Drawer from '../Drawer'
const styles = ({breakpoints}: Theme) => createStyles( {
grow: {
flexGrow: 1
},
menuButton: {
marginLeft: -12,
marginRight: 20
},
sectionDesktop: {
display: 'none',
[breakpoints.up('md')]: {
display: 'flex'
}
},
sectionMobile: {
display: 'flex'
},
})
namespace Toolbar {
interface Props {
}
export interface State {
isOpen : boolean
mobileMoreAnchorEl? : EventTarget & HTMLElement
}
export type AllProps = Props & WithStyles<typeof styles>
}
class Toolbar extends React.Component<Toolbar.AllProps, Toolbar.State> {
constructor(props: Toolbar.AllProps, context?: any) {
super(props, context);
this.state = { isOpen: false, mobileMoreAnchorEl: undefined}
}
render() {
const { classes } = this.props
// const { isOpen } = this.state
return(
<React.Fragment>
<div className={classes.sectionDesktop}>
Hello
</div>
<div className={classes.sectionMobile}>
World
</div>
</React.Fragment>
)
}
}
export default withStyles(styles)(Toolbar)
The main.tsx (a.k.a index.js) looks like this:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createBrowserHistory } from 'history';
import { configureStore } from 'app/store';
import { Router } from 'react-router';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import { App } from './app';
// prepare store
const history = createBrowserHistory()
const store = configureStore()
const theme = createMuiTheme()
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<MuiThemeProvider theme={theme} >
<App />
</MuiThemeProvider>
</Router>
</Provider>,
document.getElementById('root')
);
So, adding MuiThemeProvider does not help.
UPDATE
At the time when this answer was first written, #material-ui/styles was unstable. It is not anymore (as of v4), but it is still generally best to import from #material-ui/core/styles since the default theme will not be available when importing from #material-ui/styles.
You can read here that #material-ui/styles is unstable (alpha version).
You'll notice in my CodeSandbox that I am using:
import { withStyles, createStyles } from "#material-ui/core/styles";
instead of importing these from #material-ui/styles. When I use the same import as you, I am able to reproduce your problem.
UPDATE for v5
In v5, usage of makeStyles and withStyles is deprecated and they were removed from #material-ui/core and are only accessible via #material-ui/styles. In v5, makeStyles and withStyles do not have access to the theme unless you provide access via the ThemeProvider (and for that to work you should be using the latest v5 versions of both #material-ui/core and #material-ui/styles). Below is a working v5 example.
import React from "react";
import ReactDOM from "react-dom";
import { createTheme, ThemeProvider } from "#material-ui/core/styles";
import { withStyles } from "#material-ui/styles";
const styles = ({ breakpoints }) => ({
grow: {
flexGrow: 1
},
menuButton: {
marginLeft: -12,
marginRight: 20
},
sectionDesktop: {
display: "none",
[breakpoints.up("md")]: {
display: "flex"
}
},
sectionMobile: {
display: "flex"
}
});
const MyToolbar = (props) => {
return (
<>
<div className={props.classes.sectionDesktop}>Section Desktop</div>
{props.children}
<div className={props.classes.sectionMobile}>Section Mobile</div>
</>
);
};
const theme = createTheme();
const StyledToolbar = withStyles(styles)(MyToolbar);
function App() {
return (
<ThemeProvider theme={theme}>
<StyledToolbar>
<div>Hello</div>
<div>CodeSandbox</div>
</StyledToolbar>
</ThemeProvider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I'm stuck on exporting material-ui styles with redux connector. Here is my code:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Drawer from 'material-ui/Drawer';
import { withStyles } from 'material-ui/styles';
import PropTypes from 'prop-types';
const mapStateToProps = state => ({});
const reduxConnector = connect(mapStateToProps, null);
const styles = theme => {
console.log(theme);
return ({
paper: {
top: '80px',
boxShadow: theme.shadows[9]
},
});
};
class Cart extends Component {
render() {
const { classes } = this.props;
return (
<Drawer
open
docked
anchor="right"
classes={{ paper: classes.paper }}
>
<p style={{ width: 250 }}>cart</p>
</Drawer>
);
}
}
export default withStyles(styles, {name: 'Cart'})(Cart);
export default reduxConnector(Cart); // I want to add this
I've tried:
export default reduxConnector(withStyles(styles))(Cart); // return Uncaught TypeError: Cannot call a class as a function
export default withStyles(styles, {name: 'Cart'})(reduxConnector(Cart)); // return Uncaught Error: Could not find "store" in either the context or props of "Connect(Cart)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(Cart)".
Any solution?
Just try this
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(App));
Where App is your component.
It works fine for me.
Take a look at how it's being handled in the material-ui docs site, specifically in the AppFrame component:
export default compose(
withStyles(styles, {
name: 'AppFrame',
}),
withWidth(),
connect(),
)(AppFrame);
They're using recompose to do this.
So in your case, it would be:
import React, { Component } from 'react';
import compose from 'recompose/compose';
import { connect } from 'react-redux';
import Drawer from 'material-ui/Drawer';
import { withStyles } from 'material-ui/styles';
import PropTypes from 'prop-types';
const styles = theme => {
console.log(theme);
return {
paper: {
top: '80px',
boxShadow: theme.shadows[9],
},
};
};
const Cart = ({ classes }) =>
<Drawer open docked anchor="right" classes={{ paper: classes.paper }}>
<p style={{ width: 250 }}>cart</p>
</Drawer>;
const mapStateToProps = state => ({});
export default compose(
withStyles(styles, { name: 'Cart' }),
connect(mapStateToProps, null)
)(Cart);
Without recompose or compose:
Cart = withStyles(styles, {name: 'Cart'})(Cart);
export default reduxConnector(Cart);
install npm install recompose or yarn add recompose
and on your export section
export default compose(
withStyles(styles, {
name: 'App',
}),
connect(),
)(AppFrame);
and I forgot to export my store.
This works perfect for me
export default connect(mapStateToProps)((withStyles(styles)(ComponentNameToExport)));
You may use this below. As both withStyles and connect were higher order components
export default withStyles(styles, {name: 'Cart'})(connect(mapStateToProps, mapDispatchToProps), Cart);
Complete Component
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import ExpansionPanel from "#material-ui/core/ExpansionPanel";
import ExpansionPanelSummary from "#material-ui/core/ExpansionPanelSummary";
import ExpansionPanelDetails from "#material-ui/core/ExpansionPanelDetails";
import Typography from "#material-ui/core/Typography";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
import { withStyles } from "#material-ui/core/styles";
import { connect } from "react-redux";
import { fetchPosts } from "../../store/actions/postActions";
import PropTypes from "prop-types";
const useStyles = theme => ({
root: {
marginLeft: 250,
marginRight: 10
},
heading: {
fontSize: "1rem",
fontWeight: theme.typography.fontWeightRegular
}
});
class SimpleExpansionPanel extends React.Component {
UNSAFE_componentWillMount() {
this.props.fetchPosts();
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (nextProps.newPost) {
this.props.postsa.unshift(nextProps.newPost);
}
}
render() {
const { classes } = this.props;
const postItem = this.props.postsa.map(post => (
<ExpansionPanel key={post.id}>
<ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header">
<Typography className={classes.heading}>{post.title}</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<Typography>{post.body}</Typography>
</ExpansionPanelDetails>
</ExpansionPanel>
));
return <div className={classes.root}>{postItem}</div>;
}
}
SimpleExpansionPanel.propTypes = {
fetchPosts: PropTypes.func.isRequired,
postsa: PropTypes.array.isRequired,
newPost: PropTypes.object
};
const mapStateToProps = state => ({
postsa: state.postss.items,
newPost: state.postss.item
});
export default connect(
mapStateToProps,
{ fetchPosts }
)(withStyles(useStyles)(SimpleExpansionPanel));
I need to implement a <BackButton /> in react-admin for example when I open show page for a resource I need able to back to the list page.
Can you guide me to implement this?
I'm not familiar with react-admin routing mechanism.
Now I'm using this component in my edit form actions props:
const MyActions = ({ basePath, data, resource }) => (
<CardActions>
<ShowButton basePath={basePath} record={data} />
<CloneButton basePath={basePath} record={data} />
{/* Need <BackButton /> here */}
</CardActions>
);
export const BookEdit = (props) => (
<Edit actions={<MyActions />} {...props}>
<SimpleForm>
...
</SimpleForm>
</Edit>
);
You can use react-router-redux's goBack() function to achieve this.
For example, your button component will look something like this:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Button from '#material-ui/core/Button';
import { goBack } from 'react-router-redux';
class BackButton extends Component {
handleClick = () => {
this.props.goBack();
};
render() {
return <Button variant="contained" color="primary" onClick={this.handleClick}>Go Back</Button>;
}
}
export default connect(null, {
goBack,
})(BackButton);
Now use that button component in your CardActions.
You can get help from an example which uses react-router-redux's push() function in a similar way from the official docs.
Create a back button. This one will pass props and children (text) and uses react-router directly, which I think makes more sense and keeps your code simple.
// BackButton.js
import React from 'react'
import Button from '#material-ui/core/Button'
import { withRouter } from 'react-router'
const BackButton = ({ history: { goBack }, children, ...props }) => (
<Button {...props} onClick={goBack}>
{children}
</Button>
)
export default withRouter(BackButton)
Example usage:
import { Toolbar, SaveButton } from 'react-admin'
import BackButton from './BackButton'
const SomeToolbar = props => (
<Toolbar {...props}>
<SaveButton />
<BackButton
variant='outlined'
color='secondary'
style={{ marginLeft: '1rem' }}
>
Cancel
</BackButton>
</Toolbar>
)
The complete code is below.
//BackButton.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import compose from 'recompose/compose';
import { withStyles, createStyles } from '#material-ui/core/styles';
import { translate } from 'ra-core';
import Button from '#material-ui/core/Button';
import ArrowBack from '#material-ui/icons/ArrowBack';
import classnames from 'classnames';
import { fade } from '#material-ui/core/styles/colorManipulator';
const styles = theme =>
createStyles({
deleteButton: {
color: theme.palette.error.main,
'&:hover': {
backgroundColor: fade(theme.palette.error.main, 0.12),
// Reset on mouse devices
'#media (hover: none)': {
backgroundColor: 'transparent',
},
},
},
});
const sanitizeRestProps = ({
basePath,
className,
classes,
label,
invalid,
variant,
translate,
handleSubmit,
handleSubmitWithRedirect,
submitOnEnter,
record,
redirect,
resource,
locale,
...rest
}) => rest;
class BackButton extends Component {
static contextTypes = {
router: () => true, // replace with PropTypes.object if you use them
}
static propTypes = {
label: PropTypes.string,
refreshView: PropTypes.func.isRequired,
icon: PropTypes.element,
};
static defaultProps = {
label: 'ra.action.back',
icon: <ArrowBack />,
};
render() {
const {
className,
classes = {},
invalid,
label = 'ra.action.back',
pristine,
redirect,
saving,
submitOnEnter,
translate,
icon,
onClick,
...rest
} = this.props;
return (
<Button
onClick={this.context.router.history.goBack}
label={label}
className={classnames(
'ra-back-button',
classes.backButton,
className
)}
key="button"
{...sanitizeRestProps(rest)}>
{icon} {label && translate(label, { _: label })}
</Button>
)
}
}
const enhance = compose(
withStyles(styles),
translate
);
export default enhance(BackButton);
//Toolbar.js
import React from 'react';
import {
Toolbar,
SaveButton,
DeleteButton,
} from 'react-admin';
import { withStyles } from '#material-ui/core';
import BackButton from './BackButton'
const toolbarStyles = {
toolbar: {
display: 'flex',
justifyContent: 'space-between',
},
};
export const CustomEditToolbar = withStyles(toolbarStyles)(props => (
<Toolbar {...props}>
<SaveButton/>
<DeleteButton/>
<BackButton/>
</Toolbar>
));
export const CustomCreateToolbar = withStyles(toolbarStyles)(props => (
<Toolbar {...props}>
<SaveButton/>
<BackButton/>
</Toolbar>
));