Material-ui : darkTheme does not affect wrapped children - reactjs

My question is very simple, I want to use the Material-ui default darkTheme in a part of my app. Here is a sample of code :
<div>
<MuiThemeProvider muiTheme={getMuiTheme(darkBaseTheme)}>
<div>
<AppBar title="I am dark" />
<MyCustomComponent label="I should be dark but I am not" />
</div>
</MuiThemeProvider>
<MuiThemeProvider muiTheme={getMuiTheme(lightBaseTheme)}>
<p>I am in the lightBaseTheme (default theme)</p>
</MuiThemeProvider>
</div>
The first part of the app must be in the dark theme (that's a left menu), the second part in the light theme (that's the app itself).
The AppBar that is a direct child of the MuiThemeProvider is indeed dark, however, MyCustomComponent and its children (even when they are base Material-ui components such as RaisedButton) are not using the dark theme.
What is the simplest way to have MyCustomComponents and all its sub-children to use the dark theme too ?

You need to wrap all inside MuiThemeProvider into one element.
<MuiThemeProvider muiTheme={getMuiTheme(darkBaseTheme)}>
<div>
<AppBar title="I am dark" />
<MyCustomComponent label="I should be dark but I am not" />
</div>
</MuiThemeProvider>
actually you had to have an error like
MuiThemeProvider.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
Of course only Material-UI components inside your MyCustomComponent will have themed appearance. Everything else you need to make manually: in a way shown by Jeff McCloud or by using context like this:
function MyCustomComponent (props, context) {
const { palette } = context.muiTheme;
// Now you have access to theme settings. for example: palette.canvasColor
}
MyCustomComponent.contextTypes = {
muiTheme: React.PropTypes.object.isRequired,
};
One more way to stylize plain HTML or React Components is to wrap them into
react-theme-provider. Like this:
import ThemeProvider from 'react-theme-provider';
<MuiThemeProvider muiTheme={getMuiTheme(darkBaseTheme)}>
<div>
<AppBar title="I am dark" />
<ThemeProvider>
<MyCustomComponent label="I should be dark and I am!" />
</ThemeProvider>
</div>
</MuiThemeProvider>
reference: https://github.com/callemall/material-ui/blob/master/src/styles/MuiThemeProvider.js#L7

You need to designate that your custom component is "themeable". You do this by wrapping your component in the 'muiThemeable' Higher-Order Component:
import React from 'react';
import muiThemeable from 'material-ui/styles/muiThemeable';
class MyCustomComponent extends React.Component {
// ... your component will now have access to "this.props.muiTheme"
}
export default muiThemeable()(MyCustomComponent);
reference: http://www.material-ui.com/#/customization/themes

Related

ChakraUI useColorModeValue not updating main box color

New to react and playing around with ChakraUI. I'm using their useColorModeValue property on my main <Box> container. Though, it's not actually changing it's color, the toggle works fine for everything else, just not this box.
import {
ChakraProvider,
Box,
Text,
Link,
VStack,
Grid,
Theme,
useColorModeValue,
} from '#chakra-ui/react';
function App() {
return (
<ChakraProvider theme={theme}>
<Box minH='100vh' bg={useColorModeValue('brand.500', 'teal')}>
<Test />
<Hero />
</Box>
</ChakraProvider>
);
}
Am I missing something here?
useColorModeValue is a hook and as per the React documentation;
Always use Hooks at the top level of your React function, before any early returns. Ref.
E.G
export default function Foo() {
const bg = useColorModeValue('brand.500', 'teal')
return (<Box bg={bg} />)
}
However, you will actually need to move your Chakra provider up one level so that you can do this;
export default function Application() {
return (
<ChakraProvider theme={theme}>
<Foo />
</ChakraProvider>
)
}

How can I access themeprovider/styled-components styles from render in React/NextJS

I have my code setup working like this....
_app.js
<ThemeProvider theme={clientGroupTheme}>
<GlobalStyles />
<Layout>
<Component {...this.props} />
</Layout>
</ThemeProvider>
someComponent.js
import { createGlobalStyle } from 'styled-components'
export const LoginMenuStyles = createGlobalStyle`
.exampleClass {
color:${(props) => props.theme.secondaryColor};
}`
class LoginMenu extends React.Component {
render() {
return ( <>
<div className="exampleClass">Test</div>
<PassColorComponent myColor={${(props) => props.theme.secondaryColor}} /> //This
</>
)
}
}
Basically I want to pass that same color value as a prop (//This). It works fine in the styled-component class but I can get it to work in the render method and pass to that component.
Is it possible? am I close?
Thanks
I think 'withTheme' higher-order component is what you are looking for.
import { withTheme } from 'styled-components'
class LoginMenu extends React.Component {
render() {
return ( <>
<div className="exampleClass">Test</div>
<PassColorComponent myColor={this.props.theme.secondaryColor} />
</>
)
}
}
export default withTheme(LoginMenu);
don't forget to wrap the component at export inside the withTheme component (see bottom line). That's how the styled theme props will be passed to LoginMenu component.
The theme is available only within styled function when creating a styled component. If you want to access in React component, you could e.g. directly pass the theme down as a prop.
<Component {...this.props} theme={clientGroupTheme} />
Then, you will be able to access the theme object.
<PassColorComponent myColor={props.theme.secondaryColor} />

Why is my styling being overwritten when adding a new React Component?

I have a React component utilizing Material-UI called 'Profile', with custom CSS to create responsive font sizing like such:
let theme = createMuiTheme();
theme = responsiveFontSizes(theme);
theme.typography.h1 = {
fontSize: '5.35rem',
'#media (min-width: 600px)':{
fontSize: '3.0rem',
},
'#media (max-width: 600px)':{
fontSize: '2.2rem'
},
[theme.breakpoints.up('md')]:{
fontSize: '5.35rem'
}
}
The first few lines of the return are:
export default function Profile () {
return (
<div>
<ThemeProvider theme={theme}>
<Grid container direction="row">
<Grid item className="profile">
When I include this component in my index.js file, it renders perfectly. However, once I add a new component called 'PublicWorks.js' as such:
<React.StrictMode>
<Header />
<Profile />
<PublicWorks />
</React.StrictMode>,
the responsive styling no longer works. The code for PublicWorks is very simple, essentially just this (with appropriate closing brackets):
import React from 'react';
import {Grid, Typography, Accordion, AccordionSummary, AccordionDetails, ThemeProvider} from '#material-ui/core';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
export default function PublicWorks () {
return (
<div>
<Accordion>
<AccordionSummary expandIcon={<ExpandMoreIcon />} >
<Typography>Project 1</Typography>
</AccordionSummary>
<AccordionDetails>
Is this overriding caused by the nature of MuiTheme operating at more of a global scale, or am I doing something else incorrectly? Should I be trying something like:
<React.StrictMode>
<ThemeProvide theme={theme}>
<Header />
<Profile />
<PublicWorks />
</ThemeProvide>
</React.StrictMode>,
in order to implement this correctly?
I think the problem you are having may be correlated with this issue.
Regardless, in general you would need to use the ThemeProvider as parent of your components since it uses React Context
This component takes a theme property, and makes it available down the
React tree thanks to the context. It should preferably be used at the
root of your component tree.
Docs: https://material-ui.com/styles/api/#themeprovider
Regarding your comment And if I just want it in Profile?
You can use other solutions such as makeStyles for this if you opt to style that component separately

How to give different backgrounds to the same component?

Edited:
I am new to react, and so far couldn't find the answer to this question. I have a <Box /> component, which renders some HTML.
import React, { Component } from 'react'
class Box extends Component {
render() {
return (
<div className="box-wrapper">
<div className="different-image-for-different-copies">
</div>
<div className="box-name">
Golden Lion
</div>
</div>
)
}
}
export default Box
On one page, I have three copies of the same component. I want to be able to give unique background-image to the inner with the className different-image-for-different-copies in each of those three components without making a new component.
Component <Box /> is the child of a component <Wrapper />. What I want exactly is to be able to change the background of a certain <div> located in the child <Box /> component from the parent <Wrapper /> component. For example:
class Wrapper extends React.Component {
render() {
return (
<div>
<Box "some way to change the background of the inner div" />
</div>
)
}
}
What would be the best way to do this?
Pass you image as a props to your Box component. Then in your component, set its style like this:
<div style={{ backgroundImage: `url(${this.props.imageUrlFromProps})`}}>
</div>
That's a basic example but should help you get your image to show up.
Use a prop to pass to the component which background image it should use and set it on your component using inline style. If the set of images is limited, the rendering could instead pick a CSS class to apply.

Cannot read property 'prepareStyles' of undefined

I am trying to open a Dialog box by a button click.
When I am clicking the button the Dialog first of all is not opened and I am getting Error :
Uncaught TypeError: Cannot read property 'prepareStyles' of undefined.
Here is the code for my Component:
const muiThemebtn = getMuiTheme({
palette: {
alternateTextColor: darkBlack,
primary1Color: grey100,
}
})
export default class MyComponent extends React.Component {
constructor (props) {
super(props);
this.state = {open: true};
this.openModal = this.openModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
openModal=()=>{ this.setState({open: true}); }
closeModal=()=>{ this.setState({open: false}); }
render () {
const actions = [
<FlatButton
label="Cancel"
primary={true}
onTouchTap={this.handleClose}
/>,
<FlatButton
label="Submit"
primary={true}
keyboardFocused={true}
onTouchTap={this.handleClose}
/>,
];
return (
<div>
<MuiThemeProvider muiTheme={muiThemebtn}>
<RaisedButton label={Lang.AddUser}
onTouchTap={this.openModal}
primary={true}
display='none'
icon={<ContentAddBox color={darkBlack} style={{backgroundColor:'#e3e3e3'}}/>}
/>
</MuiThemeProvider>
<Dialog
title="Scrollable Dialog"
actions={actions}
modal={false}
open={this.state.open}
onRequestClose={this.handleClose}
autoScrollBodyContent={true}
>
Dialog Text
</Dialog>
</div>
);
}
}
Please suggest.
Note: I need to use the MuiThemeProvider
All the material-ui component must be rendered inside <MuiThemeProvider></MuiThemeProvider> tag, so we need to wrap topmost component (or at least any parent component) in material-ui's MuiThemeProvider component.
Issue is, your Dialog is outside of the MuiThemeProvider tag, put dialog also inside it, it should work.
Write it like this:
<div>
<MuiThemeProvider muiTheme={muiThemebtn}>
<RaisedButton label={Lang.AddUser}
onTouchTap={this.openModal}
primary={true}
display='none'
icon={<ContentAddBox color={darkBlack} style={{backgroundColor:'#e3e3e3'}}/>}
/>
<Dialog
title="Scrollable Dialog"
actions={actions}
modal={false}
open={this.state.open}
onRequestClose={this.handleClose}
autoScrollBodyContent={true}
>
Dialog Text
</Dialog>
</MuiThemeProvider>
</div>
Suggestion:
If you are using material ui elements in many components, then no need to put MuiThemeProvider tag on each page instead of that you can put in you homepage or better to put in index.js page, where we used to define all the routes, like this:
const muiThemebtn = getMuiTheme()
ReactDOM.render((
<MuiThemeProvider muiTheme={muiThemebtn}>
<Router history={hashHistory}>
<Route path="/" component={comp1}>
<Route path="/abc" component={comp2}/>
</Route>
</Router>
</MuiThemeProvider>
), document.getElementById('app'));
I don't have enough rep to comment on Mayank's answer but they are correct. To further elaborate on Maynak's answer, you only need to add <MuiThemeProvider></<MuiThemeProvider> to the main app container. If you do that, you should never have to worry about adding it anywhere else in your app.
Note the parent component on the left and the child component in this image:

Resources