Having a snippet ready to start a new component is something I strongly recommend. Splitting up your project into smaller components will make your code more easy to read and maintain. Having a ready-made snippet will help you with this by making it easier to start a new component.
My workflow for creating a new component is:
- Create new directory with an index.js file
- Expand snippet in index.js
- Create styles.module.css file
Tip: In PHPStorm you can enter a path eg. 'FancyBox/index.js' in the New file dialog and it creates both the folder and file.
📦 Project root
┗ 📂src
┗ 📂components
┗ 📂FancyBox
┣ 📜index.js
┗ 📜styles.module.css
My snippet contains the following code for a minimal React component:
Import React
import React from 'react'
This is needed to be able to write jsx.
concatClassNames
import { concatClassNames as cn } from '../../helpers.js'
This is especially useful when conditional classes are added to the component so I always import it. You can find out more about this here.
styles.module.css
import * as styles from './styles.module.css'
I use CSS modules in all my projects and most of my components have styles.
I stopped using import styles from './styles.module.css'
because Gatsby made importing CSS modules as ES modules their default.
The only donwside is that this does not yet work with react-scripts@5 as there seems to be an issue with that (it does work with react-scripts@4 though and there are workarounds for v5).
If you have an idea how to publish a component so it can be used by both a project loading CSS modules as ES modules and CommonJS you can help me here
Named export for the component
export function FancyBox(props) {
Making a named export for the component has some advantages over a default export. For example you can export sub components like a list item from the same location. My main component is always named exactly like the component folder.
Togehter with naming the main component file index.js
it looks awesome when importing:
import { List, ListItem } from '../components/List'
There is a small caveat to naming all your component files "index.js": The filename says nothing about the component. So when you have the file open in your editor the tab might say 'index.js' which is a little ambiguous. For me it’s not a big problem because my Editor adds the name of the Folder when two open files have the same filename.
Destructure props
const {
className,
children,
...containerProps
} = props;
The first thing I do in the actual component is to destrucure the props. containerProps
will contain ALL remaining props that are not explicitly destrucutred above this line. So if you use this component like so:
<FancyBox onClick={handleClick} id={'this-is-unique'} />
The rendered div will have a click handler and an html id. This is super useful as you can use your react component like a html element without any more code. Personally I am in love with this method, but you need to be aware of the risks.
Using the spread syntax for props makes it easy to pass invalid HTML attributes to the DOM or unwanted props to other components. React Docs
One rule that helps preventing this is to never use props.something
in your component code but always add the prop you are going to use to the destructure list
At least for me it brought me more joy and comfort than trouble.
For some components I opt out of this.
The default JSX
return <div className={cn(className, styles.fancyBox)} {...containerProps}>
{children}
</div>;
First the className from props gets concatenated with the className from the CSS modules stylesheet. Then the containerProps get applied and last the children prop ist passed to the div explicitly.
Of course I change the html element to whatever suits my component best, like a for a button and so on.
The complete snippet
import React from 'react'
import { concatClassNames as cn } from '../../helpers.js'
import * as styles from './styles.module.css'
export function FancyBox(props) {
const {
className,
children,
...containerProps
} = props;
return <div className={cn(className, styles.fancyBox)} {...containerProps}>
{children}
</div>;
}
Some tips for Jetbrains IDEs like PHPStorm
In Jetbrains IDEs it is possible to read the name of the folder and use it when naming class and function name. This makes it possible to get a snippet that automatically applies the correct names.
My variables in the Live Template are:
NAMEPASCAL
Expression: empty
Default value: groovyScript("String[] p=_editor.getVirtualFile().getPath().split('/');String prevLast = p[p.length-2];")
Skip if defined: false
NAMECAMEL
Expression: empty
Default value: camelCase(NAMEPASCAL)
Skip if defined: true
import React from 'react'
import { concatClassNames as cn } from '../../helpers.js'
import * as styles from './styles.module.css'
export function $NAMEPASCAL$(props) {
const {
className,
children,
...containerProps
} = props;
return <div className={cn(className, styles.$NAMECAMEL$)} {...containerProps}>
{children}
$END$
</div>;
}
What helps you to start new components? Let me know what you think.
Top comments (0)