2024-08-24 by Roel Kristelijn
When building React applications, it's easy to fall into the trap of over-engineering or writing components that violate basic software principles. The Bare Minimum Principles provide a practical framework for writing code that's clear, maintainable, and follows established patterns.
Let's explore how these principles apply to React component development through a real-world example.
Follow the framework's idioms and conventions
React and its ecosystem have established patterns. Fighting against them creates friction and confusion.
// ❌ Fighting React patterns class MyComponent extends Component { constructor(props) { super(props); this.state = { count: 0 }; } componentDidMount() { // Complex lifecycle logic } } // ✅ Following React idioms function MyComponent() { const [count, setCount] = useState(0); useEffect(() => { // Clear, declarative effects }, []); }
Write code that reads like mundane English
80% of programming is reading code. Make it easy.
// ❌ Unclear intent const data = items.filter(x => x.status === 'active' && x.type === 'premium'); // ✅ Clear intent const activePremiumUsers = users.filter(user => user.isActive && user.isPremium );
Simplicity is clarity by design
Don't build abstractions until you need them.
// ❌ Over-engineered const ConfigurableButton = ({ variant, size, color, theme, ...props }) => { const computedStyles = useMemo(() => generateDynamicStyles(variant, size, color, theme), [variant, size, color, theme] ); return <button style={computedStyles} {...props} />; }; // ✅ Simple and clear const PrimaryButton = ({ children, onClick }) => ( <button className="btn-primary" onClick={onClick}> {children} </button> );
Don't build it until it's needed
Avoid speculative features and abstractions.
Encapsulate complexity behind clear interfaces
// ❌ Implementation details exposed function UserProfile({ user }) { const isVip = user.subscriptionTier === 'premium' && user.accountAge > 365 && user.totalSpent > 1000; return <div>{isVip && <VipBadge />}</div>; } // ✅ Implementation hidden function UserProfile({ user }) { return <div>{user.isVip() && <VipBadge />}</div>; }
Names should reflect business intent, not implementation
// ❌ Implementation-focused names const data = fetchData(); const isValid = check(input); // ✅ Intent-focused names const userProfiles = fetchUserProfiles(); const isEmailValid = validateEmailFormat(email);
Let's analyze a real React component from our blog application:
export default function PostContent({ post }: PostContentProps) { const theme = useTheme(); const isDarkMode = theme.palette.mode === 'dark'; return ( <Box sx={{ mb: 4 }}> <Typography variant="h3" component="h1" gutterBottom> {post.title} </Typography> <Box sx={{ '& h1': { fontSize: '2rem', fontWeight: 700, mb: 2, mt: 3 }, '& h2': { fontSize: '1.75rem', fontWeight: 600, mb: 1.5, mt: 2.5 }, // ... 60+ lines of CSS-in-JS }}> <ReactMarkdown components={{ code(props) { const { className, children, ...rest } = props; const match = /language-(\w+)/.exec(className || ''); const language = match ? match[1] : ''; if (language === 'mermaid') { return <Mermaid chart={String(children).replace(/\n$/, '')} />; } if (match) { return ( <SyntaxHighlighter style={isDarkMode ? oneDark : oneLight} language={language} // ... complex configuration > {String(children).replace(/\n$/, '')} </SyntaxHighlighter> ); } return <code className={className} {...rest}>{children}</code>; }, }} > {post.content} </ReactMarkdown> </Box> </Box> ); }
Issue: Large sx prop instead of using MUI's styling patterns
// Not idiomatic MUI <Box sx={{ '& h1': { fontSize: '2rem', ... }, '& h2': { fontSize: '1.75rem', ... }, // 60+ lines }}>
Better: Use MUI's theme system and styled components
const useMarkdownStyles = () => { const theme = useTheme(); return { h1: theme.typography.h1, h2: theme.typography.h2, // Theme-based styling }; };
Issue: Component is too long (130+ lines) and mixes concerns
The component handles:
Issue: Implementation details leak into the component
// Implementation details exposed const match = /language-(\w+)/.exec(className || ''); const isDarkMode = theme.palette.mode === 'dark';
Better: Hide complexity behind clear interfaces
// Hide implementation const codeLanguage = extractLanguageFromClassName(className); const syntaxTheme = getSyntaxHighlighterTheme();
Issue: Generic, unclear names
const match = /language-(\w+)/.exec(className || ''); // What does it match? const { className, children, ...rest } = props; // What's in rest?
Better: Intention-revealing names
const languageMatch = /language-(\w+)/.exec(className || ''); const { className, children, ...otherCodeProps } = props;
Following the bare minimum principles, here's how we can improve this component:
// Hide implementation behind clear interface function CodeBlock({ className, children, ...props }) { const codeLanguage = extractLanguageFromClassName(className); if (isMermaidDiagram(codeLanguage)) { return <MermaidDiagram content={children} />; } if (hasLanguage(codeLanguage)) { return <SyntaxHighlightedCode language={codeLanguage}>{children}</SyntaxHighlightedCode>; } return <InlineCode className={className} {...props}>{children}</InlineCode>; }
// Use MUI patterns instead of large sx prop const markdownStyles = { h1: { fontSize: '2rem', fontWeight: 700, mb: 2, mt: 3 }, h2: { fontSize: '1.75rem', fontWeight: 600, mb: 1.5, mt: 2.5 }, // Clear, focused styling };
export default function PostContent({ post }) { return ( <article> <PostHeader title={post.title} date={post.date} author={post.author} /> <MarkdownContent content={post.content} /> </article> ); }
These principles aren't rigid laws - they're guidelines that prevent common pitfalls. As you gain experience, you'll learn when breaking them serves a greater purpose. But until then, following these principles will save you from future pain and make your code more maintainable.
The best engineers aren't those who never break the rules, but those who understand when and why to break them. As the legendary BOFH (Bastard Operator From Hell) would say: "The key to being a good system administrator is not to fix problems, but to prevent them from happening in the first place." The same applies to code - good principles prevent problems before they occur.
Remember: "The best code is no code at all, but if you must write code, make it so simple that even a BOFH would approve."
Next Steps: In our next post, we'll implement this refactoring step by step, showing how each principle improves code clarity and maintainability.