or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

context-state.mdfooter-toolbar.mdhelp-system.mdindex.mdlayout-configuration.mdmain-layout.mdpage-components.mdutilities.md
tile.json

context-state.mddocs/

Context and State Management

Context providers and state management system for sharing layout configuration, route information, and component state across the ProLayout ecosystem.

Capabilities

RouteContext

React Context that provides layout state and route information to all child components within the ProLayout hierarchy.

/**
 * Context providing route and layout state information
 */
const RouteContext: React.Context<RouteContextType>;

interface RouteContextType {
  /** Breadcrumb configuration mapping path to menu data */
  breadcrumb?: Record<string, MenuDataItem>;
  
  /** Complete menu data array */
  menuData?: MenuDataItem[];
  
  /** Mobile device detection */
  isMobile?: boolean;
  
  /** Sidebar collapse state */
  collapsed?: boolean;
  
  /** Sidebar visibility */
  hasSiderMenu?: boolean;
  
  /** Header visibility */
  hasHeader?: boolean;
  
  /** Sidebar width in pixels */
  siderWidth?: number;
  
  /** Page title information object */
  pageTitleInfo?: PageTitleInfo;
  
  /** Array of matched menu items for current route */
  matchMenus?: MenuDataItem[];
  
  /** Currently active menu item */
  currentMenu?: MenuDataItem;
  
  /** Layout mode */
  layout?: 'side' | 'top' | 'mix';
  
  /** Navigation theme */
  navTheme?: 'realDark' | 'light';
  
  /** Content width mode */
  contentWidth?: 'Fluid' | 'Fixed';
  
  /** Fixed header state */
  fixedHeader?: boolean;
  
  /** Primary color */
  colorPrimary?: string;
  
  /** Additional layout properties */
  [key: string]: any;
}

interface PageTitleInfo {
  /** Page title */
  title?: string;
  
  /** Page ID */
  id?: string;
  
  /** Page name */
  pageName?: string;
  
  /** Additional title info */
  [key: string]: any;
}

Usage Examples:

import React, { useContext } from "react";
import { RouteContext } from "@ant-design/pro-layout";

// Access layout state in components
function MyComponent() {
  const { 
    collapsed, 
    isMobile, 
    menuData, 
    currentMenu,
    breadcrumb 
  } = useContext(RouteContext);

  return (
    <div>
      <p>Sidebar is {collapsed ? 'collapsed' : 'expanded'}</p>
      <p>Device: {isMobile ? 'Mobile' : 'Desktop'}</p>
      <p>Menu items: {menuData?.length || 0}</p>
      <p>Current page: {currentMenu?.name || 'Unknown'}</p>
    </div>
  );
}

// Responsive component based on layout state
function ResponsiveComponent() {
  const { isMobile, collapsed, siderWidth } = useContext(RouteContext);
  
  const containerStyle = {
    marginLeft: isMobile ? 0 : (collapsed ? 80 : siderWidth),
    padding: isMobile ? 16 : 24,
  };

  return (
    <div style={containerStyle}>
      <h2>Responsive Content</h2>
      <p>This component adapts to the layout state</p>
    </div>
  );
}

// Breadcrumb component using context
function ContextBreadcrumb() {
  const { breadcrumb, matchMenus } = useContext(RouteContext);
  
  const breadcrumbItems = matchMenus?.map(menu => ({
    title: menu.name,
    path: menu.path,
  })) || [];

  return (
    <nav>
      {breadcrumbItems.map((item, index) => (
        <span key={index}>
          {index > 0 && ' > '}
          <a href={item.path}>{item.title}</a>
        </span>
      ))}
    </nav>
  );
}

// Menu-aware component
function MenuAwareComponent() {
  const { menuData, currentMenu } = useContext(RouteContext);
  
  const currentSection = currentMenu?.parentKeys?.[0];
  const sectionMenus = menuData?.filter(
    menu => menu.key === currentSection
  )?.[0]?.children || [];

  return (
    <div>
      <h3>Section Navigation</h3>
      <ul>
        {sectionMenus.map(menu => (
          <li key={menu.key}>
            <a href={menu.path}>{menu.name}</a>
          </li>
        ))}
      </ul>
    </div>
  );
}

Context Provider Pattern

RouteContext is automatically provided by ProLayout, but you can also use it independently or create custom providers.

/**
 * Custom context provider for testing or standalone usage
 */
function CustomRouteProvider({ children, value }: {
  children: React.ReactNode;
  value: Partial<RouteContextType>;
}) {
  const defaultValue: RouteContextType = {
    collapsed: false,
    isMobile: false,
    menuData: [],
    hasSiderMenu: true,
    hasHeader: true,
    siderWidth: 208,
    layout: 'side',
    navTheme: 'light',
    contentWidth: 'Fluid',
    ...value,
  };

  return (
    <RouteContext.Provider value={defaultValue}>
      {children}
    </RouteContext.Provider>
  );
}

State Management Patterns

Layout State Hook Pattern

import { useContext, useMemo } from "react";
import { RouteContext } from "@ant-design/pro-layout";

function useLayoutState() {
  const context = useContext(RouteContext);
  
  return useMemo(() => ({
    // Layout information
    isCollapsed: context.collapsed,
    isMobile: context.isMobile,
    layoutMode: context.layout,
    theme: context.navTheme,
    
    // Menu information
    menuData: context.menuData || [],
    currentMenu: context.currentMenu,
    breadcrumbPath: context.matchMenus || [],
    
    // Dimensions
    siderWidth: context.siderWidth || 208,
    contentWidth: context.isMobile ? '100%' : 
      context.collapsed ? 'calc(100% - 80px)' : 
      `calc(100% - ${context.siderWidth}px)`,
    
    // Utilities
    hasMenu: context.hasSiderMenu,
    hasHeader: context.hasHeader,
  }), [context]);
}

// Usage
function LayoutAwareComponent() {
  const { 
    isCollapsed, 
    isMobile, 
    currentMenu, 
    contentWidth 
  } = useLayoutState();
  
  return (
    <div style={{ width: contentWidth }}>
      <h1>{currentMenu?.name}</h1>
      <p>Layout is {isCollapsed ? 'collapsed' : 'expanded'}</p>
    </div>
  );
}

Menu Navigation Hook Pattern

import { useContext, useCallback } from "react";
import { RouteContext } from "@ant-design/pro-layout";

function useMenuNavigation() {
  const { menuData, breadcrumb } = useContext(RouteContext);
  
  const findMenuByPath = useCallback((path: string) => {
    const search = (menus: MenuDataItem[]): MenuDataItem | null => {
      for (const menu of menus) {
        if (menu.path === path) return menu;
        if (menu.children) {
          const found = search(menu.children);
          if (found) return found;
        }
      }
      return null;
    };
    return search(menuData || []);
  }, [menuData]);
  
  const getMenuPath = useCallback((menuKey: string) => {
    const menu = breadcrumb?.[menuKey];
    return menu?.path;
  }, [breadcrumb]);
  
  const getParentMenus = useCallback((path: string) => {
    const menu = findMenuByPath(path);
    return menu?.parentKeys?.map(key => breadcrumb?.[key]).filter(Boolean) || [];
  }, [findMenuByPath, breadcrumb]);
  
  return {
    findMenuByPath,
    getMenuPath,
    getParentMenus,
    menuData: menuData || [],
    breadcrumb: breadcrumb || {},
  };
}

// Usage
function NavigationComponent() {
  const { findMenuByPath, getParentMenus } = useMenuNavigation();
  
  const currentPath = window.location.pathname;
  const currentMenu = findMenuByPath(currentPath);
  const parentMenus = getParentMenus(currentPath);
  
  return (
    <div>
      <h2>Current: {currentMenu?.name}</h2>
      <div>
        Parent path: {parentMenus.map(p => p?.name).join(' > ')}
      </div>
    </div>
  );
}

Responsive Layout Hook Pattern

import { useContext, useEffect, useState } from "react";
import { RouteContext } from "@ant-design/pro-layout";

function useResponsiveLayout() {
  const { isMobile, collapsed, siderWidth } = useContext(RouteContext);
  const [screenSize, setScreenSize] = useState<'xs' | 'sm' | 'md' | 'lg' | 'xl'>('lg');
  
  useEffect(() => {
    const updateScreenSize = () => {
      const width = window.innerWidth;
      if (width < 576) setScreenSize('xs');
      else if (width < 768) setScreenSize('sm');
      else if (width < 992) setScreenSize('md');
      else if (width < 1200) setScreenSize('lg');
      else setScreenSize('xl');
    };
    
    updateScreenSize();
    window.addEventListener('resize', updateScreenSize);
    return () => window.removeEventListener('resize', updateScreenSize);
  }, []);
  
  return {
    isMobile,
    screenSize,
    isCollapsed: collapsed,
    siderWidth: siderWidth || 208,
    
    // Computed values
    contentMargin: isMobile ? 0 : (collapsed ? 80 : siderWidth),
    shouldShowSider: !isMobile || !collapsed,
    breakpoint: {
      xs: screenSize === 'xs',
      sm: screenSize === 'sm',
      md: screenSize === 'md',
      lg: screenSize === 'lg',
      xl: screenSize === 'xl',
    },
  };
}

// Usage
function ResponsiveGrid() {
  const { screenSize, contentMargin, breakpoint } = useResponsiveLayout();
  
  const gridCols = breakpoint.xs ? 1 : breakpoint.sm ? 2 : breakpoint.md ? 3 : 4;
  
  return (
    <div style={{
      marginLeft: contentMargin,
      display: 'grid',
      gridTemplateColumns: `repeat(${gridCols}, 1fr)`,
      gap: 16,
    }}>
      {/* Grid items */}
    </div>
  );
}

Context Integration Patterns

HOC Pattern for Context Access

import { ComponentType } from "react";
import { RouteContext, RouteContextType } from "@ant-design/pro-layout";

function withRouteContext<P extends object>(
  Component: ComponentType<P & { routeContext: RouteContextType }>
) {
  return function WrappedComponent(props: P) {
    return (
      <RouteContext.Consumer>
        {(routeContext) => (
          <Component {...props} routeContext={routeContext} />
        )}
      </RouteContext.Consumer>
    );
  };
}

// Usage
interface MyComponentProps {
  title: string;
  routeContext: RouteContextType;
}

const MyComponent = withRouteContext<{ title: string }>(
  ({ title, routeContext }: MyComponentProps) => (
    <div>
      <h1>{title}</h1>
      <p>Mobile: {routeContext.isMobile ? 'Yes' : 'No'}</p>
    </div>
  )
);

Context Testing Pattern

import { render } from "@testing-library/react";
import { RouteContext, RouteContextType } from "@ant-design/pro-layout";

function renderWithContext(
  component: React.ReactElement,
  contextValue: Partial<RouteContextType> = {}
) {
  const defaultValue: RouteContextType = {
    collapsed: false,
    isMobile: false,
    menuData: [],
    hasSiderMenu: true,
    hasHeader: true,
    siderWidth: 208,
    ...contextValue,
  };

  return render(
    <RouteContext.Provider value={defaultValue}>
      {component}
    </RouteContext.Provider>
  );
}

// Usage in tests
test('component renders correctly on mobile', () => {
  const { getByText } = renderWithContext(
    <MyComponent />,
    { isMobile: true, collapsed: true }
  );
  
  expect(getByText('Mobile view')).toBeInTheDocument();
});