Avoid double components with single element components - reactjs

I'm often creating components styled with styled-component that only consist of a single element, e.g:
const StyledButton = styled.button({
color: '#fff',
borderRadius: '2px'
})
const Button = ({ children, ...rest }) => (
<StyledButton {...rest}>{children}</StyledButton>
)
export default Button
It feels redundant to have nested components. Is there a more terse way to accomplish the above?
I've tried to do a export default, but then the React dev tools doesn't recognize the component and displays it as "Unknown".
To my understanding, it isn't possible to pass children to a styled-component?

If you just wanted to render children and pass on props, there is no need to create the extra Button component. Instead you can just export StyledButton and use it as in:
<StyledButton onClick={buttonProp}>Children you want</StyledButton>
With this approach, you might miss out on the names for components, they show up as styled.button in devtools. To overcome this, there are two methods depending on the version.
Use the babel plugin for styled components.
You can install it with
npm install --save-dev babel-plugin-styled-components
and use it by adding it to babel plugins:
{
"plugins": ["babel-plugin-styled-components"]
}
If you 4.0 or above, you can make use of the babel macro
Just change your imports to:
import styled from 'styled-components/macro';
const StyledButton = styled.button`
color: red;
`

You do not need to nest the element, you can export it directly.
<SomeComp {...rest}>{children}</SomeComp>
is equivalent to
<SomeComp {...props} />
so you do not need to separate the children and nest them.

Related

Importing/exporting styled components in React causes invalid hook calls

I am working on a React project (react#18.2.0, styled-components#5.1.1) that has been set up to use styled-components so that commonly used components are exported from a common/styled.js file, but that causes a great amount of invalid hook call errors.
Right now, it looks something like this:
export const ExampleButton = styled.button`
color: white;
`;
And then those styled componets are imported where needed, like this:
import { ExampleButton, SomeComponent, AnotherComponent } from '../common.styled';
I know they invalid hook calls are caused by this export/import setup, because the error message for one particular styled component goes away when I remove it from common/styled.js and instead paste it locally everywhere it's needed, so that instead of this:
import { ExampleButton } from '../common.styled';
const ExampleComponent = () => {
return (
<div>
<ExampleButton>Hello</ExampleButton>
</div>
);
};
I do this:
import styled from 'styled-components';
const ExampleComponent = () => {
const ExampleButton = styled.button`
color: white;
`;
return (
<div>
<ExampleButton>Hello</ExampleButton>
</div>
);
};
So that works, but it's not really a viable solution, because I would have to paste the same code everywhere, not just ExampleComponent, and doing that for the whole project would result in a massive amount of code repetition.
What is the right way to create a solution similar to common/styled.js here in a way that doesn't break the Rules of Hooks?
It seems that there had been an issue between some older versions of styled-components and React 18 that have been solved in v5.3.1.
https://github.com/styled-components/styled-components/pull/3564
Maybe upgrading styled-components to v5.3.1 or higher would solve the issue.

Material UI + styled with props throws "React does not recognize zIndex prop on a DOM element."

I'm struggling with React error -> React does not recognize zIndex prop on a DOM element.
It happens when I'm using MUI component and Styled with props.
Code example:
styled:
import {Drawer as DrawerBase, styled} from '#material-ui/core'
interface DrawerProps {
zIndex: number
}
export const Drawer = styled(DrawerBase)<Theme, DrawerProps>(({zIndex}) => ({
zIndex
}))
implementation:
<Drawer zIndex={10} />
I'm using:
"#material-ui/core": "^4.12.3"
Is there any possibility to prevent passing props to DOM elements?
Material UI's 'styled' syntax is very confusing unfortunately. This should work though:
interface DrawerProps {
zIndex: number
}
export const Drawer = styled(DrawerBase, {
shouldForwardProp: (prop) => prop !== 'zIndex',
})<Theme, DrawerProps>(({zIndex}) => ({
zIndex
}))
'shouldForwardProp' is also an unfortunate name as it sounds like the property zIndex isn't forwarded, but it is actually accessible.
Hope it helps.
I've not used styled components within MUI before, so take what I'm saying with a grain of salt. But I have a few ideas:
You're importing styled from '#material-ui/core', but you're calling a style function (without the "d").
Looking at the MUI docs for using styled components, they're using it the way we traditionally use styled components, which is using CSS syntax. I'd expect your code to look like this:
const Drawer = styled(DrawerBase)`
z-index: ${props => props.zIndex};
`;
The DOM element needs "z-index", not "zIndex".
Your code snippet also has a typo with your const Drawer where you've left out the "s": cont.
Hopefully this helps.

How to apply chakra-ui styles to custom components? (react-datepicker)

I'd like to define styles for my Datepicker component through the Chakra-ui styles.
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
import { chakra } from '#chakra-ui/react';
const StyledDatepicker = chakra(DatePicker);
I've used the chakra factory function to be able to give the StyledDatepicker some styles through props, as shown in the code above, but I'd like to define everything in a separate styles .ts file like I've done for the built-in chakra components.
I've made a Datepicker.ts file with some style definitions and added it to the overrides object which is exported here as styles:
const styles = extendTheme(overrides);
export { styles };
which is imported in App.tsx and assigned to the theme prop of my ChakraProvider.
I've made some custom components which was fine, though not exactly what I'm looking for, and I'm looking to change the styles of the header container, the element with classname react-datepicker__header - no examples of making a custom component for this on react-datepicker website. I know I can do it with CSS files, but I'd really like to do all of it in the styles file for consistency.
The answer turned out to be fairly straightforward, but imo, chakra could have made it a bit easier to learn about this possibility.
To target a css class, you just add the classname into your styles object like this:
const DatepickerStyles = {
baseStyle: {
color: 'black',
'.react-datepicker__header': {
background: 'white',
},
},
};
Then do something like this where you want to use the component (In my case, the datepicker, but this will work for any component):
function MyDatePickerComponent(props) {
const datepickerStyles = useStyleConfig('Datepicker');
return (
<Datepicker
__css={datepickerStyles}
//some required props omitted for simplicity
/>
);
}
You can use sx instead of __css, I use __css for readability.
For react-datepicker and my case of styling the header specifically, I had to use a custom calendarContainer component, where I also applied the style in a similar manner by using useMultiStyleConfig instead of useStyleConfig. Hope this can be of help to someone.

Reusing custom styles in material-ui

New to material-ui here and trying to work out the best way of using the css in js. I can see from the demo code that inserting styles as classes is pretty easy using 'withStyles' but if I do the following I'd need to re-create the 'grow' and 'defaultPadding' styles in every component.
import React from 'react';
import { withStyles } from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
function Dashboard(props) {
const { classes } = props;
return (
<Paper className={classes.defaultPadding}>
Hello
</Paper>
);
}
const styles = theme => ({
grow: {
flexGrow: 1
},
defaultPadding: {
padding: theme.spacing.unit * 3 + 'px'
}
});
export default withStyles(styles)(Dashboard);
I could easily create a factory method in an external file that returns a massive object containing all my default styles but I'd need to be able to pass the existing theme to it if I want to use things like 'theme.spacing.unit'.
So, is there a way to create a bunch of re-usable styles of my own, using theme values, then import them into any component?
Bonus question: Is it possible to create nested objects so I can do things like this? ...
className={classes.layout.header}
I think creating an external factory method would work or you could also create a ThemeProvider to pass your theme to every component that needs the same styles.
In the Codesandbox I created two Dashboard components. One with styled-components just to show how a ThemeProvider works and the other component is using withStyles.
For withStyles factory I called it sharedStyles in the demo and with this you can create a new style object inside your components by calling it and spreading it to a new style (just if you need to add more styles).
Nesting inside styles seems not supported. You could create keys like layout.heading or flatten the object before using it (see code in the demo). Both not perfect and I wouldn't use it.
I'm personally prefering styled-components but you don't need to switch to it. You can look at the source code how they implemented it and use it in your code to pass the theme to your component.
The ThemeProvider is using React Context-Api.

materialUI get class names without using withStyles

I have an app that renders components loaded dynamically from the database. I can render those components and everything works great, but I want to be able to apply specific styles to these components using withStyles.
Here's what I have tried, for sake of simplicity I'm modeling my database response with a JS file.
const dbResponse = {
__styles: {
myStyle: { background: 'blue' },
},
components: (styles) => ({
component: 'AppBar'
className: styles.myStyle,
}),
};
export default withStyles(dbResponse.__styles)(createComponentFromSchema(
dbResponse.components(dbResponse.__styles)
));
This will generate the AppBar component but not with the correct styles. The reason is obviously that what I need to pass to components is the Map that withStyles creates and passes to the children components. I could recursively clone the entire tree and replace the styles but I don't want to do that. I am using server side rendering and have access to all of the JSS specific pieces that are incidental to that scheme if it is helpful.

Resources