Components
-
Don't keep unused code in your components. Keeping dead code around can cause confusion and makes it harder to maintain the codebase. This includes:
- Commented out code blocks
- Unused imports
- Unused props
- Unused variables and functions
- Dead code paths
-
Validate the props passed to your component using type aliases or interfaces. This helps to catch bugs early and makes it easier to understand how the component is used. For example:
// Good - using TypeScript interfaces interface UserComponentProps { name: string; age: number; isAdmin?: boolean; } const UserComponent: React.FC<UserComponentProps> = ({ name, age, isAdmin = false }) => { // ... };
-
Make sure you read through a Carbon component's (opens in a new tab) documentation before using it. This helps you to understand the component's props and how to use them. It also helps you to understand the component's behavior and can obviate the need for writing custom code. For example, here's the Button component documentation (opens in a new tab).
-
Use keys in lists (opens in a new tab). This helps React to identify which items have changed, been added, or been removed. This is especially important if you are rendering a list of components that contain state.
-
Generate keys from the data itself (opens in a new tab) if possible. For example, if you are rendering a list of patients from the database, use the patient's ID as the key. This ensures that the key is unique and stable across renders.
-
Avoid using effects (opens in a new tab) for things that don't involve synchronizing with external systems. The distinction is nuanced and can be difficult to understand. Please read and internalize the linked article before reaching for effects. Common cases where you don't need to use effects include:
- Transforming data for rendering
- Handling user events
- Updating the UI based on some state
- Updating the UI based on a prop change
Scenarios where you do need to use effects include:
- Managing subscriptions or WebSocket connections
- Controlling non-React widgets that need to be initialized with a DOM element
- Managing browser APIs that React doesn't handle for you (e.g. notifications, microphone access, etc.)
- Setting up event listeners or other callbacks on window or document objects
When in doubt, ask yourself: "Is this code synchronizing with something outside of React's control?" If not, you probably don't need an effect.
-
Consider using performance optimizations like
useMemo
anduseCallback
in these specific situations:- When memoizing expensive computations.
- When passing callbacks to optimized child components that rely on referential equality to prevent unnecessary renders.
- When creating referentially stable objects or arrays that are used as dependencies in other hooks.
- When working with context providers where value changes can trigger widespread re-renders.
Don't treat these hooks as premature optimizations, but rather as tools for specific performance and stability needs. Read the React docs on useMemo (opens in a new tab) and useCallback (opens in a new tab) for detailed guidance.
-
Omit the value of a prop when it is explicitly
true
. For example:// These are equivalent <Button disabled /> // Preferred <Button disabled={true} /> // More verbose, same result
// Be explicit when you need to mark a prop falsy <Button disabled={false} />
// This pattern applies to custom components too <UserComponent isAdmin /> // Preferred <UserComponent isAdmin={true} /> // More verbose, same result <UserComponent isAdmin={false} /> // Must be explicit when false
This keeps the code cleaner and more maintainable while maintaining full functionality.
-
Keys can be used to reset the state of a component (opens in a new tab). This pattern is particularly useful when dealing with forms. When React encounters a component with a different key, it will unmount the old instance and mount a new one, effectively resetting all internal state. Examples of this include:
- Forcing a re-render of the UserProfile component when the user switches between profiles:
// Re-render when userId changes <UserProfile key={userId} userId={userId} />
- Resetting form state when switching between edit modes:
// Can be used to clear all form inputs and validation states <Form key={`${itemId}-${isEditMode}`} item={item} />
- Resetting an animation instead of transitioning from a previous state:
// Restart animation when count changes <AnimatedNumber key={count} value={count} />
The key principle is: when the key changes, React treats it as a completely different component instance, discarding all previous state. This is cleaner than manually resetting multiple state variables.
-
Follow consistent code formatting, naming conventions and folder structure. This makes the codebase more readable and easier to maintain.
-
Provide appropriate dependencies for hooks that accept dependency arrays (
useEffect
,useMemo
,useCallback
,useLayoutEffect
,useTransition
,useImperativeHandle
, anduseDebugValue
). This helps ensure that your hooks remain synchronized with the latest props and state of your component. The general rule of thumb is to:- Make dependencies as specific as possible while still capturing all values that could affect the hook's behavior
- Consider the performance implications of including objects or functions as dependencies
- Follow the React documentation guidelines (opens in a new tab) for managing object and function dependencies
- Use the
exhaustive-deps
rule fromeslint-plugin-react-hooks
to help enforce correct dependency specifications