This is a non-exhaustive list of the coding patterns the WorkWave RouteManager's front-end team follows. The patterns are based on years of experience writing, debugging, and refactoring front-end applications with React and TypeScript but evolves constantly. Most of the possible improvements and the code smells are detected during the code reviews and the pair programming sessions.
(please note: I do not work for WorkWave anymore, these patterns will not be updated)
(last update: 2022, March)
- Prefer duplication over abstraction when dealing with low complexity and boilerplate
- Avoid "return null" components
- Move long conditions out of the JSX
- Get JSX combinations/states clear
- Prefer CSS Variables to optimize Material UI styles
- Create component's explicit variants
- Avoid naming props with CSS property-like names
Prefer duplication over abstraction when dealing with low complexity and boilerplate
Don't get code "smarter" just for the sake of avoiding a bit of duplication. Often, duplication improves readability and gets code ready for future changes.
// ❌ don't
const filterValues = {
all: 'all',
winners: 'winner',
losers: 'loser',
}
function FilterBar(props) {
const isActive = (filter) => filter === props.currentFilter;
const filters = Object.values(filterValues);
return filters.map(filter =>
<div className={isActive(filter) ? 'active' : ''}>{filter}</div>
)
}
// ✅ do
function FilterBar(props) {
const { currentFilter } = props
return <>
<div className={currentFilter === 'all' ? 'active' : ''}>all</div>
<div className={currentFilter === 'winner' ? 'active' : ''}>winner</div>
<div className={currentFilter === 'loser' ? 'active' : ''}>loser</div>
</>
}
Avoid "return null" components
The component's parent decides if the component is rendered or not.
// ❌ don't
function Poll() {
const [pollEnabled, setPollEnabled] = useState(false)
return <>
<TogglePoll setPollEnabled={setPollEnabled} />
<PollConfirmation pollEnabled={pollEnabled} />
</>
}
function PollConfirmation(props) {
if(!props.pollEnabled) return null
return <>Poll is enabled</>
}
// ✅ do
function Poll() {
const [pollEnabled, setPollEnabled] = useState(false)
return <>
<TogglePoll setPollEnabled={setPollEnabled} />
{pollEnabled && <PollConfirmation />}
</>
}
function PollConfirmation() {
return <>Poll is enabled</>
}
Move long conditions out of the JSX
Components' JSX can get unreadable in a while, always move out long conditions.
// ❌ don't
function Foo() {
/* ... rest of the code... */
return <>
{(cond1 && status === 'bar' || date === today) && <Bar />}
{(cond2 && cond3 date !== today) && <Baz />}
</>
}
// ✅ do
function Foo() {
/* ... rest of the code... */
const renderBar = cond1 && status === 'bar' || date === today
const renderBaz = cond2 && cond3 date !== today
return <>
{renderBar && <Bar />}
{renderBaz && <Baz />}
</>
}
Get JSX combinations/states clear
JSX with a lot ternaries and logic conditions can make simple cases hard to read. The reader will spend a significant amount of mental energies to detect what is rendered and what isn't.
// ❌ don't
function Theme(props) {
const { theme } = props
return <>
{theme === 'dark' ? <div>
<BlackBackgroundButton />
<GreyBackgroundButton />
</div> : <div>
<WhiteBackgroundButton />
<SepiaBackgroundButton />
</div>}
<ColorsPalette theme={theme} />
{theme === 'dark' ? <DarkThemeSample /> : <LightThemeSample />}
{theme === 'light' && <LightThemesList />}
</>
}
// ✅ do
function Theme(props) {
const { theme } = props
switch(theme) {
case 'dark':
return <>
<div>
<BlackBackgroundButton />
<GreyBackgroundButton />
</div>
<ColorsPalette theme='dark' />
<DarkThemeSample />
</>
case 'light':
return <>
<div>
<WhiteBackgroundButton />
<SepiaBackgroundButton />
</div>
<ColorsPalette theme='light' />
<LightThemeSample />
<LightThemesList />
</>
}
}
The next step is returning a new, dedicated component, for every switch case.
// ❌ don't
function Theme(props) {
const { theme } = props
switch(theme) {
case 'dark':
return <>
<div>
<BlackBackgroundButton />
<GreyBackgroundButton />
</div>
<ColorsPalette theme='dark' />
<DarkThemeSample />
</>
case 'light':
return <>
<div>
<WhiteBackgroundButton />
<SepiaBackgroundButton />
</div>
<ColorsPalette theme='light' />
<LightThemeSample />
<LightThemesList />
</>
}
}
// ✅ do
function Theme(props) {
switch(props.theme) {
case 'dark':
return <DarkTheme />
case 'light':
return <LightTheme />
}
}
Prefer CSS Variables to optimize Material UI styles
Material UI injects a lot of styles into the DOM. CSS variables limit the amount of injected styles for dynamic properties.
// ❌ don't
const useStyles = makeStyles({
foo: props => ({
backgroundColor: props.backgroundColor,
}),
});
function MyComponent(props) {
const classes = useStyles(props);
return <div className={classes.foo} />
}
// ✅ do
const useClasses = makeStyles({
foo: { backgroundColor: 'var(--background-color)' }
})
function useStyles(backgroundColor: string) {
const classes = useClasses()
const styles = useMemo(() => {
return {
foo: { '--background-color': `${backgroundColor}` } as CSSProperties,
}
}, [backgroundColor])
return [classes, styles] as const
}
function MyComponent(props) {
const [classes, styles] = useStyles(props.backgroundColor)
return <div className={classes.foo} styles={styles.foo} />
}
Create component's explicit variants
Component explicit variants are more predictable and easy to refactor.
// ❌ don't
function Consumer() {
return (
<>
<Button>Click me</Button>
<Button src="./image.jog" />
</>
)
}
// ✅ do
function Consumer() {
return (
<>
<Button variant="text">Click me</Button>
<Button variant="image" src="./image.jog" />
</>
)
}
Avoid naming props with CSS property-like names
Props named like CSS properties create ambiguity, the reader cannot understand if the prop is used to directly set the CSS property or not.
// ❌ don't
function Consumer() {
return (
<Stack align="left" /> // is "align" used to set CSS Flex' align?
)
}
// ✅ do
function Consumer() {
return (
<Stack variant="left" /> // variant does not sound like a CSS property, nice!
)
}
Top comments (0)