๐ Reusable Best Practices Framework
๐ Overviewโ
This guide establishes comprehensive reusable best practices to prevent technical debt and maintain clean, consistent code across the Juniro design system. These standards build upon our existing design system documentation and ensure scalability, maintainability, and code quality.
๐ฏ Core Principlesโ
1. DRY (Don't Repeat Yourself)โ
- Use shared handlers and utilities
- Create reusable components
- Consolidate common patterns
2. Single Responsibilityโ
- Each component has one clear purpose
- Functions do one thing well
- Clear separation of concerns
3. Type Safety Firstโ
- Strict TypeScript usage
- Proper interface definitions
- Compile-time error prevention
4. Design System Consistencyโ
- Always use design system components
- Follow established patterns
- Maintain visual consistency
๐๏ธ Component Architecture Standardsโ
Component Structure Templateโ
import React, { useState, useCallback, useEffect } from 'react';
import { ComponentNameProps } from './ComponentName.types';
/**
* ComponentName - Brief description of component purpose
*
* @example
* ```tsx
* <ComponentName
* variant="primary"
* onEvent={handleEvent}
* />
* ```
*
* @param props - Component props
* @param props.variant - Visual variant of the component
* @param props.onEvent - Event handler callback
* @returns JSX element
*/
export const ComponentName: React.FC<ComponentNameProps> = ({
variant = 'default',
onEvent,
className = '',
children,
...props
}) => {
// State declarations (group related state)
const [internalState, setInternalState] = useState(initialValue);
// Event handlers (use useCallback for performance)
const handleInternalEvent = useCallback((param: string) => {
console.log('Internal event:', param);
if (onEvent) {
onEvent(param);
}
}, [onEvent]);
// Effects (clear dependencies)
useEffect(() => {
// Effect logic
}, [dependencies]);
return (
<div
className={`base-classes ${className}`}
{...props}
>
{children}
</div>
);
};
ComponentName.displayName = 'ComponentName';
Props Interface Standardsโ
export interface ComponentNameProps {
/** Primary content */
children?: React.ReactNode;
/** Visual variant */
variant?: 'default' | 'primary' | 'secondary';
/** Size variant */
size?: 'sm' | 'md' | 'lg';
/** Whether component is disabled */
disabled?: boolean;
/** Event handlers */
onEvent?: (param: string) => void;
/** Additional CSS classes */
className?: string;
/** Required props (list first) */
requiredProp: string;
/** Optional props (list after required) */
optionalProp?: string;
}
๐ Event Handler Standardsโ
Use Shared Handlers Patternโ
// โ
GOOD: Use shared handlers from utils
import { useDefaultHandlers } from '../../utils/withDefaultHandlers';
export const MyComponent: React.FC<MyComponentProps> = ({
onCustomEvent,
...props
}) => {
const defaultHandlers = useDefaultHandlers();
return (
<Header
onSearch={defaultHandlers.handleSearch}
onLoginClick={onCustomEvent || defaultHandlers.handleLoginClick}
onSignupClick={defaultHandlers.handleSignupClick}
onJoinAsProviderClick={defaultHandlers.handleJoinAsProviderClick}
/>
);
};
Custom Handler Patternโ
// โ
GOOD: Custom handlers with proper typing and logging
const handleCustomAction = useCallback((param: string) => {
console.log('Custom action triggered:', param);
// Validation
if (!param.trim()) {
console.warn('Empty parameter provided to handleCustomAction');
return;
}
// Business logic
const processedParam = param.toLowerCase();
// Call external handler
if (onCustomAction) {
onCustomAction(processedParam);
}
}, [onCustomAction]);
Form Handler Patternโ
// โ
GOOD: Separate handlers for different input types
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
};
const handleSelectChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const handleTextareaChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
๐ State Management Standardsโ
State Declaration Patternโ
// โ
GOOD: Group related state
const [formData, setFormData] = useState({
field1: '',
field2: '',
field3: false
});
const [uiState, setUiState] = useState({
isLoading: false,
error: '',
isOpen: false
});
const [dataState, setDataState] = useState({
items: [],
selectedItem: null,
filters: {}
});
State Update Patternโ
// โ
GOOD: Immutable state updates
const handleFieldChange = (field: string, value: string) => {
setFormData(prev => ({
...prev,
[field]: value
}));
};
// โ
GOOD: Complex state updates
const handleItemSelect = (itemId: string) => {
setDataState(prev => ({
...prev,
selectedItem: prev.items.find(item => item.id === itemId) || null
}));
};
๐จ Design System Component Usageโ
Always Use Design System Componentsโ
// โ
GOOD: Use design system components
import {
Button,
Input,
Select,
FormInput,
Checkbox,
Textarea,
Pagination,
RangeSlider,
LoadingSpinner,
DotIndicator
} from '../../shared';
// โ BAD: Native HTML elements
<input type="text" className="..." />
<button className="..." />
// โ
GOOD: Design system components
<Input type="text" placeholder="Enter text" />
<Button variant="primary" size="md">Click me</Button>
<Select options={options} value={value} onChange={handleChange} />
Component Composition Patternโ
// โ
GOOD: Compose components properly
<div className="form-container">
<FormInput
type="email"
name="email"
value={formData.email}
onChange={handleInputChange}
error={fieldErrors.email}
showSuccessIcon={!!formData.email && !fieldErrors.email}
/>
<Checkbox
name="agreeToTerms"
checked={formData.agreeToTerms}
onChange={handleInputChange}
label="I agree to the terms and conditions"
/>
<Button
type="submit"
disabled={isLoading}
loading={isLoading}
>
Submit
</Button>
</div>
๐ TypeScript Standardsโ
Strict Typingโ
// โ
GOOD: Explicit typing
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
// Handler logic
};
// โ
GOOD: Proper interface extension
interface MyComponentProps extends React.HTMLAttributes<HTMLDivElement> {
customProp: string;
optionalProp?: number;
onCustomEvent?: (param: string) => void;
}
Type Assertionsโ
// โ
GOOD: Safe type assertions
const currentData = dataSets[currentTab as keyof typeof dataSets];
// โ
GOOD: Proper array typing
const items: Array<{ id: string; name: string }> = [];
// โ
GOOD: Union type handling
const handleVariantChange = (variant: 'primary' | 'secondary' | 'outline') => {
setCurrentVariant(variant);
};
Generic Typesโ
// โ
GOOD: Generic component types
interface ListProps<T> {
items: T[];
renderItem: (item: T, index: number) => React.ReactNode;
keyExtractor: (item: T) => string;
}
export const List = <T,>({ items, renderItem, keyExtractor }: ListProps<T>) => {
return (
<div>
{items.map((item, index) => (
<div key={keyExtractor(item)}>
{renderItem(item, index)}
</div>
))}
</div>
);
};
๐ Documentation Standardsโ
JSDoc Templateโ
/**
* ComponentName - Brief description of what the component does
*
* @example
* ```tsx
* <ComponentName
* variant="primary"
* onEvent={handleEvent}
* />
* ```
*
* @param props - Component props
* @param props.variant - Visual variant of the component
* @param props.onEvent - Event handler callback
* @param props.children - Child elements
* @returns JSX element
*
* @since 1.0.0
* @author Development Team
*/
Storybook Templateโ
const meta: Meta<typeof ComponentName> = {
title: 'design-system/component-name',
component: ComponentName,
parameters: {
layout: 'centered',
docs: {
description: {
component: 'A reusable component that provides X functionality with Y features.',
},
},
},
argTypes: {
variant: {
control: 'select',
options: ['default', 'primary', 'secondary'],
description: 'Visual variant of the component',
},
size: {
control: 'select',
options: ['sm', 'md', 'lg'],
description: 'Size of the component',
},
disabled: {
control: 'boolean',
description: 'Whether the component is disabled',
},
},
tags: ['autodocs'],
};
๐งช Testing Standardsโ
Test Structureโ
import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ComponentName } from './ComponentName';
describe('ComponentName', () => {
// โ
GOOD: Test component rendering
it('renders correctly', () => {
render(<ComponentName>Test content</ComponentName>);
expect(screen.getByText('Test content')).toBeInTheDocument();
});
// โ
GOOD: Test user interactions
it('handles user interactions', async () => {
const user = userEvent.setup();
const handleEvent = jest.fn();
render(<ComponentName onEvent={handleEvent}>Click me</ComponentName>);
await user.click(screen.getByText('Click me'));
expect(handleEvent).toHaveBeenCalled();
});
// โ
GOOD: Test accessibility
it('meets accessibility standards', () => {
render(<ComponentName aria-label="Test component">Content</ComponentName>);
expect(screen.getByLabelText('Test component')).toBeInTheDocument();
});
});
๐ Performance Standardsโ
Optimization Patternsโ
// โ
GOOD: Use useCallback for event handlers
const handleClick = useCallback((id: string) => {
console.log('Item clicked:', id);
if (onItemClick) {
onItemClick(id);
}
}, [onItemClick]);
// โ
GOOD: Use useMemo for expensive calculations
const expensiveValue = useMemo(() => {
return items.filter(item => item.active).map(item => item.value);
}, [items]);
// โ
GOOD: Lazy load components
const LazyComponent = lazy(() => import('./LazyComponent'));
// โ
GOOD: Memoize components
const MemoizedComponent = memo(ComponentName);
๐ File Organization Standardsโ
Directory Structureโ
src/
โโโ components/
โ โโโ shared/ # Design system components
โ โ โโโ ComponentName/
โ โ โ โโโ ComponentName.tsx
โ โ โ โโโ ComponentName.types.ts
โ โ โ โโโ ComponentName.stories.tsx
โ โ โ โโโ ComponentName.test.tsx
โ โ โ โโโ index.ts
โ โ โโโ index.ts
โ โโโ public/ # Page components
โ โโโ HomePage/
โ โโโ ActivityDiscovery/
โ โโโ ...
โโโ utils/ # Shared utilities
โ โโโ withDefaultHandlers.tsx
โ โโโ pageHandlers.ts
โ โโโ index.ts
โโโ hooks/ # Custom hooks
โ โโโ useForm.ts
โ โโโ useLocalStorage.ts
โ โโโ index.ts
โโโ styles/ # Global styles
โโโ globals.css
โโโ components.css
๐ Code Review Checklistโ
Component Reviewโ
- Uses design system components
- Follows naming conventions
- Has proper TypeScript typing
- Includes JSDoc documentation
- No unused variables/functions
- Uses shared handlers where appropriate
- Proper error handling
- Responsive design considerations
- Accessibility compliance
- Performance optimizations
Storybook Reviewโ
- All props documented in argTypes
- Interactive stories for complex components
- Accessibility stories included
- Performance considerations documented
- Examples match real-world usage
- Component description is clear and helpful
Test Reviewโ
- Tests cover main functionality
- User interaction tests included
- Accessibility tests included
- Edge cases covered
- Tests are readable and maintainable
๐ Maintenance Standardsโ
Regular Cleanup Tasksโ
- Remove unused imports weekly
- Check for unused variables monthly
- Update documentation with new features
- Review and update shared handlers quarterly
- Audit design system component usage
- Update TypeScript types as needed
Performance Monitoringโ
- Monitor bundle size
- Check component render performance
- Review memory usage
- Optimize based on metrics
๐ Git Commit Standardsโ
Commit Message Templateโ
type(scope): brief description
- Detailed changes
- Breaking changes (if any)
- Related issues
Examples:
feat(components): add new Button variant
fix(forms): resolve type errors in form handlers
refactor(utils): consolidate shared event handlers
docs(storybook): update component documentation
test(components): add accessibility tests
perf(components): optimize render performance
Branch Namingโ
feature/component-name
fix/issue-description
refactor/component-name
docs/documentation-update
test/test-coverage
๐ฏ Implementation Checklistโ
Before Creating New Componentsโ
- Check if similar component exists in design system
- Use shared handlers from
utils/withDefaultHandlers - Follow component naming conventions
- Add proper TypeScript interfaces
- Include JSDoc documentation
- Plan Storybook stories
Before Adding New Propsโ
- Check if prop already exists in interface
- Add proper TypeScript typing
- Update Storybook argTypes
- Add JSDoc documentation
- Consider if prop should be optional or required
- Test with different prop combinations
Before Adding Event Handlersโ
- Check if handler exists in shared handlers
- Use consistent naming pattern (
handleEventName) - Add proper TypeScript typing
- Include error handling
- Add console.log for debugging
- Consider performance implications
๐จ Anti-Patterns to Avoidโ
โ Don't Do Thisโ
// โ BAD: Native HTML elements
<input type="text" className="..." />
<button className="..." />
// โ BAD: Inline styles
<div style={{ color: 'red', fontSize: '16px' }} />
// โ BAD: Any types
const handleEvent = (param: any) => { ... };
// โ BAD: Unused variables
const [unusedState, setUnusedState] = useState('');
// โ BAD: Missing error handling
const handleSubmit = () => {
// No error handling
submitData();
};
โ Do This Insteadโ
// โ
GOOD: Design system components
<Input type="text" placeholder="Enter text" />
<Button variant="primary">Click me</Button>
// โ
GOOD: Tailwind classes
<div className="text-red-500 text-base" />
// โ
GOOD: Proper typing
const handleEvent = (param: string) => { ... };
// โ
GOOD: Remove unused code
// const [unusedState, setUnusedState] = useState(''); // Removed
// โ
GOOD: Error handling
const handleSubmit = async () => {
try {
await submitData();
} catch (error) {
console.error('Submit failed:', error);
setError('Failed to submit data');
}
};
๐ Success Metricsโ
Code Quality Metricsโ
- Zero TypeScript errors in main code
- 100% design system component usage
- 90%+ test coverage
- All components documented in Storybook
- No unused imports or variables
- Consistent code formatting
Performance Metricsโ
- Bundle size under target threshold
- Component render time < 16ms
- Memory usage stable
- Lighthouse score > 90
Maintenance Metricsโ
- Code review time < 30 minutes
- Bug fixes < 5% of total commits
- Documentation always up to date
- Team adoption of standards > 95%
Remember: These best practices are living guidelines. They should evolve with the project and team needs. Regular reviews and updates ensure they remain relevant and effective.