Loading content…
Loading content…
Master component patterns and best practices
import { ReactNode } from "react";
import styles from "./Button.module.css";
interface ButtonProps {
children: ReactNode;
onClick: () => void;
variant?: "primary" | "secondary";
disabled?: boolean;
}
export const Button = ({
children,
onClick,
variant = "primary",
disabled = false,
}: ButtonProps) => {
return (
<button
className={`${styles.button} ${styles[variant]}`}
onClick={onClick}
disabled={disabled}
>
{children}
</button>
);
};
// ❌ Monolithic component
const UserCard = () => {
return (
<div>
<img src={user.avatar} />
<h2>{user.name}</h2>
<p>{user.bio}</p>
<button>Follow</button>
</div>
);
};
// ✅ Composed from smaller components
const UserCard = ({ user }) => {
return (
<div>
<UserAvatar src={user.avatar} />
<UserInfo name={user.name} bio={user.bio} />
<FollowButton userId={user.id} />
</div>
);
};
Senior Developer Wisdom
// Hook for managing form state
const useForm = (initialValues) => {
const [values, setValues] = useState(initialValues);
const handleChange = (e) => {
const { name, value } = e.target;
setValues((prev) => ({ ...prev, [name]: value }));
};
const reset = () => setValues(initialValues);
return { values, handleChange, reset };
};
// Using the hook
const LoginForm = () => {
const { values, handleChange } = useForm({ email: "", password: "" });
return (
<form>
<input
name="email"
value={values.email}
onChange={handleChange}
/>
<input
name="password"
type="password"
value={values.password}
onChange={handleChange}
/>
</form>
);
};
const Tabs = ({ children }) => {
const [active, setActive] = useState(0);
return (
<div>
<TabButtons>
{React.Children.map(children, (child, i) => (
<TabButton key={i} active={i === active} onClick={() => setActive(i)}>
{child.props.label}
</TabButton>
))}
</TabButtons>
{children[active]}
</div>
);
};
// Usage
<Tabs>
<Tab label="Profile">Profile content</Tab>
<Tab label="Settings">Settings content</Tab>
</Tabs>
const MouseTracker = ({ render }) => {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
const handleMouseMove = (e) => {
setPosition({ x: e.clientX, y: e.clientY });
};
window.addEventListener("mousemove", handleMouseMove);
return () => window.removeEventListener("mousemove", handleMouseMove);
}, []);
return render(position);
};
// Usage
<MouseTracker
render={(pos) => <p>Mouse at: {pos.x}, {pos.y}</p>}
/>
Pro Tip
// Without Fragment
const List = () => {
return (
<div>
<h2>Items</h2>
<ul>...</ul>
</div>
);
};
// With Fragment (no extra wrapper)
const List = () => {
return (
<>
<h2>Items</h2>
<ul>...</ul>
</>
);
};
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong</h1>;
}
return this.props.children;
}
}
// Usage
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
Common Pitfall
// Memoize to prevent unnecessary re-renders
const ExpensiveComponent = React.memo(({ data }) => {
return <div>{data.name}</div>;
});
// useCallback for stable function references
const Component = ({ onSelect }) => {
const handleSelect = useCallback((item) => {
onSelect(item.id);
}, [onSelect]);
return <List onSelect={handleSelect} />;
};
Component Patterns
Marking it complete updates your roadmap progress percentage.