“Clean code” can mean anything. In a React (or React Native) codebase, here’s what I actually aim for—concrete, not vague.
1. One Job Per Component
A component should do one thing: render a list, show a form, display a card. If it’s doing “fetch data + transform + render + handle three different states,” I split it. Smaller components are easier to read, test, and reuse. I extract hooks for logic (data fetching, form state) so the component stays mostly presentational.
2. Names That Describe Purpose
Components: UserCard, OrderSummary, LoginForm—names that say what they are. Functions: formatCurrency, getUserDisplayName, validateEmail—names that say what they do. I avoid Wrapper, Container, or Component1 unless there’s no better name. Good names reduce the need for comments.
3. Fewer Props, Clear Contracts
I keep the prop list short. If a component needs many props, I consider: is it doing too much? Can some props be grouped (e.g. user: { name, avatar })? I use TypeScript so the contract is explicit. Optional props are optional for a reason; I don’t add “just in case” props.
4. No Magic in the Tree
I avoid inline object/array literals and inline functions in JSX when they’re used as props to children. They break referential equality and cause unnecessary re-renders. I use useMemo / useCallback when the value or handler is passed to a memoized child; otherwise I keep them outside the tree or in stable refs. No “it works but I don’t know why” performance.
5. State in the Right Place
I keep state as close as possible to where it’s used. If only one component needs it, it lives there. If two siblings need it, I lift to the parent (or use a small context). I don’t put everything in a global store “for later.” Server state goes in TanStack Query (or similar), not in Redux or Context by default.
6. Boundaries for Side Effects
Data fetching, subscriptions, and other side effects live in useEffect (or in a library like TanStack Query). I don’t fetch in render or mix side effects with pure rendering logic. Cleanup (unsubscribe, abort) goes in the effect’s return. That keeps components predictable and avoids leaks.
7. Consistency Over Personal Taste
I follow the project’s existing patterns: file structure, naming, where state lives, how we handle errors. I don’t introduce a new pattern “because I like it” without team agreement. Consistency makes the codebase navigable for everyone.
Clean code, to me, is: one job per piece, clear names, explicit contracts, minimal magic, state and effects in the right place, and consistency. Boring and repeatable—and that’s the point.
Saad Mehmood — Portfolio
Top comments (0)