Styled-Components Not Loading Until Page Refreshed - reactjs

I'm facing an issue where the css from my styled-components aren't loading until we refresh. I pared down a specific file until there is just a button. I kept the file flow, but stripped out practically everything but the button and the green background color.
I would expect that on the first load, the button has a green background. Instead, it has a transparent background. But it does have the green background after refresh.
If additional context helps, we are using material themes at the moment but are transitioning to styled components. This specific page doesn't reference the MUI themes in any way I can tell. That said, I also am getting the multiple #mui/styles warning in the console. (My package.json doesn't directly use #mui/styles, so I am assuming it's a dependency)
I'm currently stumped and would happily take any suggestions on what to check.
It looks like there are several instances of `#material-ui/styles` initialized in this application.
This may cause theme propagation issues, broken class names, specificity issues, and makes your application bigger without a good reason.
See https://material-ui.com/r/styles-instance-warning for more info.
index.tsx
import PrimaryButton from "components/Shared/Button/PrimaryButton";
export default function ReviewPurchaseOrderPage() {
return (
<PrimaryButton
text={"Approve"}
onClick={() => console.log("test for SO question")}
/>
);
}
PrimaryButton.tsx
import ActionButton from "components/Shared/Button/ActionButton";
interface Props {
text: string;
onClick: () => void;
}
export default function PrimaryButton({
text,
onClick,
}: Props) {
return (
<ActionButton
text={text}
onClick={onClick}
/>
);
}
ActionButton.tsx
import { Button } from "#material-ui/core";
import styled from "styled-components";
const StyledActionButton = styled(Button)`
background-color: #769362;
`;
interface Props {
text: string;
onClick: () => void;
}
export default function ActionButton({
text,
onClick,
}: Props) {
return (
<StyledActionButton
onClick={onClick}
>
{text}
</StyledActionButton>
);
}

Related

React - App component not updating theme correctly

I am trying to implement a theme toggle feature in my React application using the ThemeContext and the useState hook. I have created two themes, "light" and "dark", and a ThemeContext object to make the current theme available to all child components. I have also created an App component that wraps the provided component with the ThemeContext.Provider and a toggle button to switch between the two themes.
The problem is that, when I click the toggle button, the theme is not changing correctly. The button click seems to be registered, but the theme is not updating. I have tried different approaches, but I can't seem to find the issue.
Here is my _app.js file:
`
import { ThemeContext } from '../behaviour/context';
import { useState } from 'react';
function App({ Component, pageProps }) {
const [currentTheme, setCurrentTheme] = useState(ThemeContext.themes.light);
function toggleTheme() {
setCurrentTheme(currentTheme === ThemeContext.themes.light ? ThemeContext.themes.dark :ThemeContext.themes.light);
}
return (
<ThemeContext.Provider value={currentTheme}>
<button onClick={toggleTheme}>Toggle theme</button>
<Component {...pageProps} />
</ThemeContext.Provider>
);
}
export default App;
`
And here is my context.js file defined inside of a folder named behaviour:
`
import { createContext } from 'react';
export const themes = {
light: {
background: 'white',
text: 'black'
},
dark: {
background: 'black',
text: 'white'
}
};
export const ThemeContext = createContext(themes);
`
Please let me know if you see anything wrong with my code or if you have any suggestions for how to fix this issue.
I tried to use createGlobalStyle from styled-components but I confused myself so I removed it.

Rendered Component doesn't show in storybook

I want to render MUI Button Component in my storybook and then change MUI attributes(variant, color, disabled, etc.) from storybook. I could get result when working on simple plain html button but it doesn't work on MUI one. The current result is it doesn't show any error in terminal, nor in browser. When I type something in args it's visible on Storybook Controls but Button itself doesn't show up itself. Here is the code of MyNewButton.tsx file.
import Button from "#mui/material/Button"
export const MyNewButton = () => {
return <Button />
}
and here is the code of MyNewButton.stories.tsx
import { ComponentMeta, ComponentStory } from "#storybook/react"
import { MyNewButton } from "./MyNewButton"
export default {
title: "MyComponents/MyNewButton",
component: MyNewButton,
} as ComponentMeta<typeof MyNewButton>
const Template: ComponentStory<typeof MyNewButton> = (args) => (
<MyNewButton {...args} />
)
export const First = Template.bind({})
First.args = {
label: "MyNewButton",
backgroundColor: "red",
}
and Here is the storybook screenshot.
So, I can see you are overlooking some basic steps and I'm going to point them out to you. Your Storybook is rendering fine.
First, you are rendering a button from MUI without a label/text. You are supposed to have an opening tag and a closing tag for the button.
so, it should be <Button>Hello World!</Button> and not <Button />.
After fixing that, the next step is to use your args in your MyNewButton component so that storybook will know what to change your background color to.
import { FC } from "react";
type ButtonProps = {
backgroundColor: string;
label: string;
};
export const MyNewButton: FC<ButtonProps> = (props) => {
const { backgroundColor, label } = props;
const style = {
backgroundColor,
};
return (
<Button variant="contained" style={style}>
{label}
</Button>
);
};
Your button should be rendering fine correctly now and to make change the background in your app, you just have to pass in the props like so:
<MyNewButton backgroundColor={"green"} label={"My New Button"} />

Second class of multiple classes can't override style

button.module.css
.general_button {
width: 100%;
}
button.js
import React from 'react';
import styles from './button.module.css';
const GeneralButton = ({ text, className}) => {
return (
<button className={`${styles.general_button} ${className}`}>
<p className={styles.text}>{text}</p>
</button>
);
};
export { GeneralButton };
app.module.css
.next_btn {
width: 35%;
}
app.js
import React from 'react';
import classes from './app.module.css';
import { GeneralButton } from './components/Buttons';
const App = () => {
return (
<div>
<GeneralButton className={classes.next_btn} text='next' />
</div>
)
}
export default App;
When I used button component from app, button component is working with 'width:100%'. I would like to be button component is with 'width: 35%'.
This is when I inspect browser.
This is when I inspect browser.
Since both classes are equally specific, your app chooses whichever comes later in the stylesheet. Unfortunately you have much control over this order since it is likely generated by webpack (or whatever bundler you happen to be using).
One of the main benefits of using modular css is to avoid having to fight specificity battles such as this, so I would suggest reworking the button component slightly - something like this might work:
const GeneralButton = ({ text, variant }) => {
let buttonClass = styles.general_button;
if (variant === "next") {
buttonClass = styles.next_button;
}
return (
<button className={buttonClass}>
<p className={styles.text}>{text}</p>
</button>
);
};
The advantage here is that the button lays out the options for how to present it and the parent component just selects the type it needs. This makes it safer to re-work the button component in the future since it encapsulates all of it's states.

How can I pass my custom theme from a react project with material-ui to an equal one but that only has reusable components transpiled with roll-up?

I want to reuse components of a react project that are transpiled with roll-up. Each of the components have the "makeStyles" function of material-ui to be customized from where they are called.
I currently have a project and I want to use these components, but despite the fact that I include ThemeProvider, I am not reading properties such as color palettes, and use the one with the default material-ui.
I have the reusable component project at the root of the new project. I added it as a local dependency, and then I do a test including some of the reusable components like the button.
It renders me well, even with the properties that happened to it, the reusable component (button) reads them to me and it works. But it doesn't use any of the custom properties of the theme.
Código del botón (Commons Project)
const useStyles = makeStyles( theme => {
console.log(theme); // <-- no theme custom variables
return {
root: {
width: '100%',
},
};
});
const Button = (props) => {
const { title, outlined, theme, color } = props;
const classes = useStyles(props);
return (
<Button variant="contained"
{...props}>
{ title }
</Button>
);
};
Código que llama al botón (New Project)
import React, { useState } from 'react';
import { ThemeProvider} from '#material-ui/styles';
import { Button} from 'commons-lib';
import theme from './theme';
function App() {
return (
<ThemeProvider theme={theme}>
<div className="App">
<Button id="btn12" color="primary" title="Aceptar" size="medium" />
</div>
</ThemeProvider>
);
}
export default App;
code theme
const theme = createMuiTheme({
palette: {
primary: purple,
secondary: {
light:'#b1b',
main:'#066',
dark:'#838',
contrastText: '#fff'
}
}
},
});
export default theme;
It is not using the palette that I defined, but it is using the default palette. From the console I can see that the themeprovider properties are correct, but in the button console.log the default palette appears.
I want you to use the palette that is being injected from the new project.
The problem arises because the root project was using "#material-ui/styles" of the "commons-lib" project, so the latter installed the default theme.
The solution was to make an
npm link commons-lib/node_modules/#material-ui/styles
from the root project.

How to change button props based on breakpoints in Material UI

I'm trying to change the variant and/or size on a material-ui button for different screen sizes. For example, use no variant or size="small" below the "sm" breakpoint and variant="outlined" and/or size="large" above "sm".
Normally, I'd make use withStyles and create a style with theme.breakpoints to affect changes by applying the style to the element using className, however, variant and size are props.
After reading the api, scouring the web, and fiddling extensively, I can't seem to figure out any straight-forward way to change the props based on viewport width.
I've thought about creating a "width-detector" and then using some JS logic to change the button element's props accordingly, but that seems just a bit far out as a solution.
So I'm asking here to see if there is an easier solution out there. Thanks.
The withWidth HOC is deprecated per Material UI Docs.
This is the approach that works now, with a combination of useTheme and useMediaQuery.
Edit: useTheme is not really required here, since useMediaQuery automatically provides that as an argument.
// import { useTheme } from "#material-ui/core/styles";
import { useMediaQuery } from "#material-ui/core";
...
function ResponsiveButton() {
// const theme = useTheme();
// const isSmallScreen = useMediaQuery(theme.breakpoints.down("xs"));
const isSmallScreen = useMediaQuery(theme => theme.breakpoints.down("xs"));
const buttonProps = {
variant: isSmallScreen ? "text" : "outlined",
size: isSmallScreen ? "small" : "large"
};
return (
<Button {...buttonProps} color="primary">
Responsive Button
</Button>
);
}
export default ResponsiveButton;
Material UI Docs:
Sometimes you might want to change the React rendering tree based on the breakpoint value. We provide a withWidth() higher-order component for this use case.
withWidth injects a width property into your component that gives you access to the current breakpoint value. This allows you to render different props or content based on screen size.
function ResponsiveButton({ width }) {
// This is equivalent to theme.breakpoints.down("sm")
const isSmallScreen = /xs|sm/.test(width);
const buttonProps = {
variant: isSmallScreen ? "text" : "outlined",
size: isSmallScreen ? "small" : "large"
};
return (
<Button {...buttonProps} color="primary">
Responsive Button
</Button>
);
}
export default withWidth()(ResponsiveButton);
This is my implementation
import Button from "#material-ui/core/Button";
import { useTheme } from "#material-ui/core/styles";
import useMediaQuery from "#material-ui/core/useMediaQuery";
import { useEffect } from "react";
const ResponsiveButton = (props) => {
const theme = useTheme();
const desktop = useMediaQuery(theme.breakpoints.up("lg"));
const tablet = useMediaQuery(theme.breakpoints.up("sm"));
const mobile = useMediaQuery(theme.breakpoints.up("xs"));
const sizes = () => {
if (desktop) return "large";
if (tablet) return "medium";
if (mobile) return "small";
};
return <Button {...props} size={sizes()}/>;
};
export default ResponsiveButton;
To all those who are from 'mui' and not 'material-ui' age, you can create two different components and pass display as an in-line style. Only one will be rendered for a specific screen size.
Refer the example below.
<Typography
sx={{ display:{sx:'none', sm:'none', md:'block', lg:'block', xl:'block'} }}
>
This will be rendered only on screens which are medium-sized and above
</Typography>
<Typography
sx={{ display:{sx:'block', sm:'block', md:'none', lg:'none', xl:'none'} }}
>
This will be rendered only on screens which are below medium-sized
</Typography>
Not sure if you must mention all the sizes inside display. Maybe you can skip lg and xl once you set md to 'block'.

Resources