๐ŸŽจComponent Architecture and Styling

Master modern component structure, styles, and reusable UI systems.

๐Ÿ“ Component Design Patterns

Welcome to Component Design Patterns! In this chapter, we'll explore various approaches to building maintainable and reusable React components. Understanding these patterns will help you create more organized, scalable, and efficient UIs.

๐Ÿ’ก Presentational vs Container Components

React applications often use two types of components: presentational and container. Understanding the difference between them is crucial for creating clean, maintainable code.

  • Presentational Components: Focus on rendering UI (what to display). They don't manage state or interact with data sources.
  • Container Components: Manage business logic, state, and interactions. They pass props to presentational components.
// Presentational Component
function UserCard({ user }) {
  return (
    <div className='user-card'>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}

// Container Component
function UserList() {
  const [users, setUser] = useState([]);

  useEffect(() => {
    fetchUsers().then(data => setUser(data));
  }, []);

  return (
    <div className='user-list'>
      {users.map(user => (
        <UserCard key={user.id} user={user} />
      ))}
    </div>
  );
}

๐Ÿ’ก Compound Components

A compound component is a set of related, reusable components that work together to fulfill a specific purpose. This pattern promotes code reuse and modularity.

  • Break down functionality into smaller, focused components.
  • Use composition to build complex UIs from simple parts.
  • Encourage reusability across different contexts.
// Compound Component: Pagination
function Pagination({ currentPage, totalPages }) {
  return (
    <div className='pagination'>
      {renderPrevious(currentPage)}
      {renderPageNumbers(currentPage, totalPages)}
      {renderNext(currentPage, totalPages)}
    </div>
  );
}

function renderPrevious(currentPage) {
  if (currentPage === 1) return null;
  return <button onClick={() => setCurrentPage(currentPage - 1)}>โ† Previous</button>;
}

// And other helper components...

๐Ÿ’ก Higher-Order Components (HOCs)

A higher-order component is a function that takes a component and returns a new component with enhanced functionality. HOCs are powerful for reusing logic across multiple components.

  • Add common functionality like authentication, loading states, or data fetching.
  • Enhance components without changing their core behavior.
  • Promote code reuse and separation of concerns.
// HOC: withLoadingState
function withLoadingState(Component) {
  return function WrappedComponent({ fetchFn, ...props }) {
    const [loading, setLoading] = useState(true);
    
    useEffect(() => {
      async function loadData() {
        await fetchFn();
        setLoading(false);
      }
      loadData();
    }, []);

    return (
      <div className='loading-container'>
        {loading ? (
          <div className='loader'>Loading...</div>
        ) : (
          <Component {...props} />
        )}
      </div>
    );
  };
}

๐Ÿ’ก Render Props Pattern

The render props pattern is a way to reuse component logic without changing the component's render output. It provides flexibility by allowing components to customize how they display data.

  • Useful for creating reusable UI components with customizable rendering.
  • Encourages loose coupling between components and their presentation layer.
  • Ideal for complex or conditional rendering scenarios.
// Render Props Example
function DataFetcher({ url, render }) {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(data => setData(data));
  }, [url]);

  return (
    <div className='data-container'>
      {render({ data })}
    </div>
  );
}

// Usage in another component
<DataFetcher url='/api/users' 
  render={({ data }) => (
    <div className='user-list'>
      {data.map(user => (
        <UserCard key={user.id} user={user} />
      ))}
    </div>
  )} />

๐Ÿ’ก Key Considerations

  • Choose the right pattern based on your use case and requirements.
  • Keep components small, focused, and reusable.
  • Avoid overcomplicating component hierarchies.
  • Use composition and props to pass data and behavior.

โœ… Best Practices

  • Separate presentation from business logic.
  • Use compound components for complex UIs.
  • Leverage HOCs for reusable functionality.
  • Employ render props for flexible rendering.

โŒ Common Pitfalls to Avoid

  • Don't mix presentation and container logic in a single component.
  • Avoid creating overly complex component hierarchies.
  • Don't use HOCs for simple use cases where props would suffice.
  • Don't overuse render props when simpler alternatives exist.

๐Ÿ’ก Real-World Applications

  • Building reusable UI kits with compound components.
  • Implementing authentication across multiple components using HOCs.
  • Creating dynamic dashboards with render props for custom visualization.

๐Ÿ’… Styling Strategies

When it comes to styling React applications, there are multiple approaches you can take. Each has its own pros and cons, and choosing the right one depends on your project's needs.

๐Ÿ’ก Common Styling Methods

  • CSS Modules: Allow you to write CSS in a modular way with local scope.
  • Styled Components: Uses JSX for styling, making it easy to compose styles.
  • Tailwind CSS: A utility-first approach that leverages CSS classes directly in your markup.
  • Emotion: Combines the power of CSS Modules and styled components.

๐Ÿ’ก Key Principles for Scalable Design Systems

  • Use BEM naming conventions to avoid conflicts.
  • Leverage CSS variables for consistent theming.
  • Create reusable components with atomic design.
  • Maintain a component library with design tokens.
// Example of CSS Module
.example {
  padding: 20px;
}

// Styled Component Example
const Button = styled.button`
  background-color: ${props => props.primary ? '#4CAF50' : '#f1f1f1'};
`

๐Ÿ’ก Avoiding Global CSS Conflicts

  • Use CSS Modules with scoped selectors.
  • Prefix utility classes to prevent name collisions.
  • Leverage Webpack's MiniCssExtractPlugin for code splitting.

๐Ÿ’ก Optimizing CSS Performance

  • Minify and compress CSS files.
  • Use critical-path rendering for above-the-fold content.
  • Leverage browser caching effectively.

โœ… Best Practices for Modern Applications

  • Use CSS-in-JS solutions like Styled Components or Emotion.
  • Adopt a utility-first framework like Tailwind CSS.
  • Create component-specific styles to maintain encapsulation.

Quiz

Question 1 of 11

What is the main difference between presentational and container components?

  • Presentational components manage state
  • Container components handle rendering logic
  • Presentational components focus on UI display
  • Both are the same