costco-grocery-list/docs/archive/code-cleanup-guide.md
2026-01-27 00:03:58 -08:00

11 KiB

Code Cleanup Guide

This guide documents the cleanup patterns and best practices applied to the codebase, starting with GroceryList.jsx. Use this as a reference for maintaining consistent, clean, and readable code across all files.

Table of Contents

  1. Spacing & Organization
  2. Comment Formatting
  3. Code Simplification
  4. React Performance Patterns
  5. Cleanup Checklist

Spacing & Organization

Two-Line Separation

Use 2 blank lines to separate logical groups and functions.

Before:

const handleAdd = async (itemName, quantity) => {
  // function body
};
const handleBought = async (id) => {
  // function body
};

After:

const handleAdd = async (itemName, quantity) => {
  // function body
};


const handleBought = async (id) => {
  // function body
};

Logical Grouping

Organize code into clear sections:

  • State declarations
  • Data loading functions
  • Computed values (useMemo)
  • Event handlers grouped by functionality
  • Helper functions
  • Render logic

Comment Formatting

Section Headers

Use the === Section Name === format for major sections, followed by 2 blank lines before the next code block.

Pattern:

// === State ===
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(true);


// === Data Loading ===
const loadItems = async () => {
  // implementation
};


// === Event Handlers ===
const handleClick = () => {
  // implementation
};

Common Section Names

  • === State ===
  • === Data Loading ===
  • === Computed Values === or === Sorted Items Computation ===
  • === Event Handlers === or specific groups like === Item Addition Handlers ===
  • === Helper Functions ===
  • === Render ===

Code Simplification

1. Optional Chaining

Replace && null checks with optional chaining when accessing nested properties.

Before:

if (existingItem && existingItem.bought === false) {
  // do something
}

After:

if (existingItem?.bought === false) {
  // do something
}

When to use:

  • Accessing properties on potentially undefined/null objects
  • Checking nested properties: user?.profile?.name
  • Method calls: item?.toString?.()

When NOT to use:

  • When you need to check if the object exists first (use explicit check)
  • For boolean coercion: if (item) is clearer than if (item?.)

2. Ternary Operators

Use ternary operators for simple conditional assignments and returns.

Before:

let result;
if (condition) {
  result = "yes";
} else {
  result = "no";
}

After:

const result = condition ? "yes" : "no";

When to use:

  • Simple conditional assignments
  • Inline JSX conditionals
  • Return statements with simple conditions

When NOT to use:

  • Complex multi-line logic (use if/else for readability)
  • Nested ternaries (hard to read)

3. Early Returns

Use early returns to reduce nesting.

Before:

const handleSuggest = async (text) => {
  if (text.trim()) {
    // long implementation
  } else {
    setSuggestions([]);
    setButtonText("Add Item");
  }
};

After:

const handleSuggest = async (text) => {
  if (!text.trim()) {
    setSuggestions([]);
    setButtonText("Add Item");
    return;
  }
  
  // main implementation without nesting
};

4. Destructuring

Use destructuring for cleaner variable access.

Before:

const username = user.username;
const email = user.email;
const role = user.role;

After:

const { username, email, role } = user;

5. Array Methods Over Loops

Prefer array methods (.map(), .filter(), .find()) over traditional loops.

Before:

const activeItems = [];
for (let i = 0; i < items.length; i++) {
  if (!items[i].bought) {
    activeItems.push(items[i]);
  }
}

After:

const activeItems = items.filter(item => !item.bought);

React Performance Patterns

1. useCallback for Event Handlers

Wrap event handlers in useCallback to prevent unnecessary re-renders of child components.

const handleBought = useCallback(async (id, quantity) => {
  await markBought(id);
  setItems(prevItems => prevItems.filter(item => item.id !== id));
  loadRecentlyBought();
}, []); // Add dependencies if needed

When to use:

  • Handler functions passed as props to memoized child components
  • Functions used as dependencies in other hooks
  • Functions in frequently re-rendering components

2. useMemo for Expensive Computations

Use useMemo for computationally expensive operations or large transformations.

const sortedItems = useMemo(() => {
  const sorted = [...items];
  
  if (sortMode === "az") {
    sorted.sort((a, b) => a.item_name.localeCompare(b.item_name));
  }
  
  return sorted;
}, [items, sortMode]);

When to use:

  • Sorting/filtering large arrays
  • Complex calculations
  • Derived state that's expensive to compute

3. React.memo for Components

Wrap components with React.memo and provide custom comparison functions to prevent unnecessary re-renders.

const GroceryListItem = React.memo(
  ({ item, onClick, onLongPress }) => {
    // component implementation
  },
  (prevProps, nextProps) => {
    return (
      prevProps.id === nextProps.id &&
      prevProps.item_name === nextProps.item_name &&
      prevProps.quantity === nextProps.quantity &&
      prevProps.item_image === nextProps.item_image
    );
  }
);

When to use:

  • List item components that render frequently
  • Components with stable props
  • Pure components (output depends only on props)

4. In-Place State Updates

Update specific items in state instead of reloading entire datasets.

Before:

const handleUpdate = async (id, newData) => {
  await updateItem(id, newData);
  loadItems(); // Reloads entire list from server
};

After:

const handleUpdate = useCallback(async (id, newData) => {
  const response = await updateItem(id, newData);
  
  setItems(prevItems =>
    prevItems.map(item =>
      item.id === id ? { ...item, ...response.data } : item
    )
  );
}, []);

Benefits:

  • Faster updates (no server round-trip for entire list)
  • Preserves scroll position
  • Better user experience (no full re-render)

Cleanup Checklist

Use this checklist when cleaning up a file:

Structure & Organization

  • Group related state variables together
  • Use 2-line spacing between logical sections
  • Add section comments using === Format ===
  • Order sections logically (state → data loading → computed → handlers → helpers → render)

Code Simplification

  • Replace && null checks with optional chaining where appropriate
  • Convert simple if/else to ternary operators
  • Use early returns to reduce nesting
  • Apply destructuring for cleaner variable access
  • Use array methods instead of loops

React Performance

  • Wrap stable event handlers in useCallback
  • Use useMemo for expensive computations
  • Consider React.memo for list items or frequently re-rendering components
  • Update state in-place instead of reloading from server

Consistency

  • Check naming conventions (camelCase for functions/variables)
  • Ensure consistent spacing and indentation
  • Remove unused imports and variables
  • Remove console.logs (except intentional debugging aids)

Testing After Cleanup

  • Verify no functionality broke
  • Check that performance improved (using React DevTools Profiler)
  • Test all interactive features
  • Verify mobile/responsive behavior still works

Example: Before & After

Before Cleanup

import { useState, useEffect } from "react";

export default function MyComponent() {
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(true);
  const loadItems = async () => {
    setLoading(true);
    const res = await getItems();
    setItems(res.data);
    setLoading(false);
  };
  useEffect(() => {
    loadItems();
  }, []);
  const handleUpdate = async (id, data) => {
    await updateItem(id, data);
    loadItems();
  };
  const handleDelete = async (id) => {
    await deleteItem(id);
    loadItems();
  };
  if (loading) return <p>Loading...</p>;
  return (
    <div>
      {items.map(item => (
        <Item key={item.id} item={item} onUpdate={handleUpdate} onDelete={handleDelete} />
      ))}
    </div>
  );
}

After Cleanup

import { useCallback, useEffect, useMemo, useState } from "react";


export default function MyComponent() {
  // === State ===
  const [items, setItems] = useState([]);
  const [loading, setLoading] = useState(true);


  // === Data Loading ===
  const loadItems = async () => {
    setLoading(true);
    const res = await getItems();
    setItems(res.data);
    setLoading(false);
  };


  useEffect(() => {
    loadItems();
  }, []);


  // === Event Handlers ===
  const handleUpdate = useCallback(async (id, data) => {
    const response = await updateItem(id, data);
    
    setItems(prevItems =>
      prevItems.map(item =>
        item.id === id ? { ...item, ...response.data } : item
      )
    );
  }, []);


  const handleDelete = useCallback(async (id) => {
    await deleteItem(id);
    setItems(prevItems => prevItems.filter(item => item.id !== id));
  }, []);


  // === Render ===
  if (loading) return <p>Loading...</p>;

  return (
    <div>
      {items.map(item => (
        <Item 
          key={item.id} 
          item={item} 
          onUpdate={handleUpdate} 
          onDelete={handleDelete} 
        />
      ))}
    </div>
  );
}

Key improvements:

  1. Added section comments for clarity
  2. Proper 2-line spacing between sections
  3. Wrapped handlers in useCallback for performance
  4. In-place state updates instead of reloading entire list
  5. Better import organization

Additional Resources


Notes

  • Don't over-optimize: Not every component needs useCallback/useMemo. Apply these patterns when you have measurable performance issues or when working with large lists.
  • Readability first: If a simplification makes code harder to understand, skip it. Code should be optimized for human reading first.
  • Test thoroughly: Always test after cleanup to ensure no functionality broke.
  • Incremental cleanup: Don't try to clean up everything at once. Focus on one file at a time.

Last Updated: Based on GroceryList.jsx cleanup (January 2026)