אנליטיק לוגו
אנליטיק
חזרה

Advanced React Patterns

Explore advanced patterns and techniques for React development.

Advanced React Patterns

As your React applications grow in complexity, you’ll need to employ advanced patterns to maintain clean, scalable, and performant code. This guide explores some of the most powerful patterns available in modern React development.

Compound Components Pattern

The compound components pattern allows you to create components that work together to form a complete UI element:

function Accordion() {
  return (
    <Accordion.Root>
      <Accordion.Item value="item1">
        <Accordion.Header>
          <Accordion.Trigger>What is React?</Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content>
          React is a JavaScript library for building user interfaces.
        </Accordion.Content>
      </Accordion.Item>
    </Accordion.Root>
  );
}

This pattern provides excellent flexibility while maintaining a clean API.

Render Props Pattern

Render props allow you to share code between components using a prop whose value is a function:

function DataFetcher({ render }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchData().then(result => {
      setData(result);
      setLoading(false);
    });
  }, []);

  return render({ data, loading });
}

// Usage
<DataFetcher 
  render={({ data, loading }) => 
    loading ? <Spinner /> : <DataDisplay data={data} />
  } 
/>

Higher-Order Components (HOCs)

HOCs are functions that take a component and return a new component with additional functionality:

function withAuth(WrappedComponent) {
  return function AuthenticatedComponent(props) {
    const { user } = useAuth();
    
    if (!user) {
      return <LoginForm />;
    }
    
    return <WrappedComponent {...props} user={user} />;
  };
}

// Usage
const ProtectedDashboard = withAuth(Dashboard);

Custom Hooks Pattern

Custom hooks let you extract component logic into reusable functions:

function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      return initialValue;
    }
  });

  const setValue = value => {
    try {
      setStoredValue(value);
      window.localStorage.setItem(key, JSON.stringify(value));
    } catch (error) {
      console.error(error);
    }
  };

  return [storedValue, setValue];
}

// Usage
function Settings() {
  const [theme, setTheme] = useLocalStorage('theme', 'light');
  
  return (
    <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      Switch to {theme === 'light' ? 'dark' : 'light'} mode
    </button>
  );
}

Context + Reducer Pattern

Combine Context API with useReducer for complex state management:

const AppContext = createContext();

function appReducer(state, action) {
  switch (action.type) {
    case 'SET_USER':
      return { ...state, user: action.payload };
    case 'SET_THEME':
      return { ...state, theme: action.payload };
    default:
      return state;
  }
}

function AppProvider({ children }) {
  const [state, dispatch] = useReducer(appReducer, {
    user: null,
    theme: 'light'
  });

  return (
    <AppContext.Provider value={{ state, dispatch }}>
      {children}
    </AppContext.Provider>
  );
}

function useAppContext() {
  const context = useContext(AppContext);
  if (!context) {
    throw new Error('useAppContext must be used within AppProvider');
  }
  return context;
}

Performance Patterns

Memoization

Use React.memo, useMemo, and useCallback strategically:

const ExpensiveComponent = React.memo(({ data, onItemClick }) => {
  const processedData = useMemo(() => {
    return data.map(item => ({
      ...item,
      processed: expensiveProcessing(item)
    }));
  }, [data]);

  const handleClick = useCallback((id) => {
    onItemClick(id);
  }, [onItemClick]);

  return (
    <div>
      {processedData.map(item => (
        <Item 
          key={item.id} 
          data={item} 
          onClick={handleClick}
        />
      ))}
    </div>
  );
});

Code Splitting

Split your code to improve initial load times:

import { lazy, Suspense } from 'react';

const LazyComponent = lazy(() => import('./LazyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

Error Boundaries

Handle errors gracefully in your React tree:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    console.error('Error caught by boundary:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

// Usage
<ErrorBoundary>
  <App />
</ErrorBoundary>

Conclusion

These advanced patterns will help you build more maintainable, performant, and scalable React applications. Remember to choose the right pattern for your specific use case and always prioritize code readability and maintainability.

Table of Contents

Magic UI

Try Magic UI Pro

Beautiful design system

Learn more