Advanced Components in Coherent.js

November 17, 2025 ยท View on GitHub

This guide covers advanced component patterns, optimization techniques, and complex use cases in Coherent.js. Learn how to build sophisticated, reusable, and performant components.

๐Ÿš€ Higher-Order Components (HOCs)

Basic HOC Pattern

Higher-Order Components are functions that take a component and return a new component with additional functionality:

const withLoading = (WrappedComponent) => {
  return withState({ isLoading: false })(({ state, stateUtils, ...props }) => {
    const { setState } = stateUtils;
    
    if (state.isLoading) {
      return {
        div: {
          className: 'loading-container',
          children: [
            { div: { className: 'spinner' } },
            { p: { text: 'Loading...' } }
          ]
        }
      };
    }
    
    return WrappedComponent({
      ...props,
      setLoading: (isLoading) => setState({ isLoading })
    });
  });
};

// Usage
const DataComponent = ({ data, setLoading }) => ({
  div: {
    children: [
      { h2: { text: 'Data Display' } },
      {
        button: {
          text: 'Load Data',
          onclick: async () => {
            setLoading(true);
            await fetchData();
            setLoading(false);
          }
        }
      }
    ]
  }
});

const LoadingDataComponent = withLoading(DataComponent);

Composable HOCs

Chain multiple HOCs together for complex functionality:

const withAuth = (WrappedComponent) => {
  return withState({ user: null })(({ state, stateUtils, ...props }) => {
    if (!state.user) {
      return {
        div: {
          className: 'auth-required',
          children: [
            { h2: { text: 'Authentication Required' } },
            { button: { text: 'Login', onclick: () => showLogin() } }
          ]
        }
      };
    }
    
    return WrappedComponent({ ...props, user: state.user });
  });
};

const withErrorBoundary = (WrappedComponent) => {
  return withState({ hasError: false, error: null })(({ state, stateUtils, ...props }) => {
    const { setState } = stateUtils;
    
    if (state.hasError) {
      return {
        div: {
          className: 'error-boundary',
          children: [
            { h2: { text: 'Something went wrong' } },
            { p: { text: state.error?.message } },
            {
              button: {
                text: 'Try Again',
                onclick: () => setState({ hasError: false, error: null })
              }
            }
          ]
        }
      };
    }
    
    try {
      return WrappedComponent({
        ...props,
        onError: (error) => setState({ hasError: true, error })
      });
    } catch (error) {
      setState({ hasError: true, error });
      return { div: { text: 'Error occurred' } };
    }
  });
};

// Compose multiple HOCs
const EnhancedComponent = withAuth(withErrorBoundary(withLoading(DataComponent)));

Context Provider HOC

Create context-like functionality with HOCs:

const withTheme = (WrappedComponent) => {
  return withState({ 
    theme: 'light',
    colors: {
      primary: '#007bff',
      secondary: '#6c757d',
      background: '#ffffff'
    }
  })(({ state, stateUtils, ...props }) => {
    const { setState } = stateUtils;
    
    const toggleTheme = () => {
      const newTheme = state.theme === 'light' ? 'dark' : 'light';
      const newColors = newTheme === 'dark' ? {
        primary: '#0d6efd',
        secondary: '#adb5bd',
        background: '#212529'
      } : {
        primary: '#007bff',
        secondary: '#6c757d',
        background: '#ffffff'
      };
      
      setState({ theme: newTheme, colors: newColors });
    };
    
    return WrappedComponent({
      ...props,
      theme: state.theme,
      colors: state.colors,
      toggleTheme
    });
  });
};

๐Ÿ”„ Component Composition Patterns

Render Props Pattern

Pass rendering logic as props:

const DataFetcher = ({ url, children }) => {
  return withState({ 
    data: null, 
    loading: false, 
    error: null 
  })(({ state, stateUtils }) => {
    const { setState } = stateUtils;
    
    const fetchData = async () => {
      setState({ loading: true, error: null });
      try {
        const response = await fetch(url);
        const data = await response.json();
        setState({ data, loading: false });
      } catch (error) {
        setState({ error: error.message, loading: false });
      }
    };
    
    if (typeof children === 'function') {
      return children({
        data: state.data,
        loading: state.loading,
        error: state.error,
        refetch: fetchData
      });
    }
    
    return { div: { text: 'No render function provided' } };
  });
};

// Usage
const App = () => ({
  div: {
    children: [
      DataFetcher({
        url: '/api/users',
        children: ({ data, loading, error, refetch }) => {
          if (loading) return { div: { text: 'Loading...' } };
          if (error) return { div: { text: `Error: ${error}` } };
          
          return {
            div: {
              children: [
                { h2: { text: 'Users' } },
                { button: { text: 'Refresh', onclick: refetch } },
                data ? {
                  ul: {
                    children: data.map(user => ({
                      li: { key: user.id, text: user.name }
                    }))
                  }
                } : null
              ].filter(Boolean)
            }
          };
        }
      })
    ]
  }
});

Compound Components

Create components that work together:

const Tabs = withState({ activeTab: 0 })(({ state, stateUtils, children }) => {
  const { setState } = stateUtils;
  
  const childrenArray = Array.isArray(children) ? children : [children];
  const tabPanels = childrenArray.filter(child => child.type === 'TabPanel');
  const tabLabels = childrenArray.filter(child => child.type === 'TabList');
  
  return {
    div: {
      className: 'tabs-container',
      children: [
        // Tab navigation
        {
          ul: {
            className: 'tab-nav',
            children: tabPanels.map((panel, index) => ({
              li: {
                key: index,
                className: `tab ${state.activeTab === index ? 'active' : ''}`,
                children: [
                  {
                    button: {
                      text: panel.props.label,
                      onclick: () => setState({ activeTab: index })
                    }
                  }
                ]
              }
            }))
          }
        },
        
        // Active tab content
        {
          div: {
            className: 'tab-content',
            children: [
              tabPanels[state.activeTab]?.children || { div: { text: 'No content' } }
            ]
          }
        }
      ]
    }
  };
});

const TabPanel = ({ label, children }) => ({
  type: 'TabPanel',
  props: { label },
  children
});

// Usage
const App = () => ({
  div: {
    children: [
      Tabs({
        children: [
          TabPanel({
            label: 'Tab 1',
            children: { div: { text: 'Content for tab 1' } }
          }),
          TabPanel({
            label: 'Tab 2',
            children: { div: { text: 'Content for tab 2' } }
          })
        ]
      })
    ]
  }
});

Slot Pattern

Create components with named slots:

const Card = ({ header, body, footer, ...props }) => ({
  div: {
    className: `card ${props.className || ''}`,
    style: props.style,
    children: [
      header ? {
        div: {
          className: 'card-header',
          children: Array.isArray(header) ? header : [header]
        }
      } : null,
      
      body ? {
        div: {
          className: 'card-body',
          children: Array.isArray(body) ? body : [body]
        }
      } : null,
      
      footer ? {
        div: {
          className: 'card-footer',
          children: Array.isArray(footer) ? footer : [footer]
        }
      } : null
    ].filter(Boolean)
  }
});

// Usage
const UserCard = ({ user }) => Card({
  header: { h3: { text: user.name } },
  body: [
    { p: { text: `Email: ${user.email}` } },
    { p: { text: `Role: ${user.role}` } }
  ],
  footer: [
    { button: { text: 'Edit', onclick: () => editUser(user.id) } },
    { button: { text: 'Delete', onclick: () => deleteUser(user.id) } }
  ]
});

๐ŸŽฏ Dynamic Components

Component Registry

Create a system for dynamic component loading:

const componentRegistry = new Map();

const registerComponent = (name, component) => {
  componentRegistry.set(name, component);
};

const getComponent = (name) => {
  return componentRegistry.get(name);
};

const DynamicComponent = ({ type, props = {} }) => {
  const Component = getComponent(type);
  
  if (!Component) {
    return {
      div: {
        className: 'error',
        text: `Component "${type}" not found`
      }
    };
  }
  
  return Component(props);
};

// Register components
registerComponent('Button', ({ text, onClick }) => ({
  button: { text, onclick: onClick }
}));

registerComponent('Input', ({ value, onChange, placeholder }) => ({
  input: { 
    type: 'text',
    value, 
    oninput: onChange,
    placeholder
  }
}));

// Usage
const FormBuilder = ({ fields }) => ({
  form: {
    children: fields.map(field => DynamicComponent({
      type: field.type,
      props: field.props
    }))
  }
});

const dynamicForm = FormBuilder({
  fields: [
    { type: 'Input', props: { placeholder: 'Name' } },
    { type: 'Input', props: { placeholder: 'Email' } },
    { type: 'Button', props: { text: 'Submit' } }
  ]
});

Conditional Component Loading

const ConditionalRenderer = ({ condition, components }) => {
  if (typeof condition === 'function') {
    const result = condition();
    return components[result] || components.default || { div: { text: 'No match' } };
  }
  
  return components[condition] || components.default || { div: { text: 'No match' } };
};

// Usage
const UserDisplay = ({ user, isAdmin }) => ConditionalRenderer({
  condition: () => {
    if (!user) return 'guest';
    if (isAdmin) return 'admin';
    return 'user';
  },
  components: {
    guest: { div: { text: 'Please log in' } },
    user: { div: { text: `Welcome, ${user.name}` } },
    admin: { div: { text: `Admin: ${user.name}` } },
    default: { div: { text: 'Unknown user type' } }
  }
});

๐Ÿ”„ Async Components

Lazy Loading Components

const LazyComponent = ({ loader, fallback, ...props }) => {
  return withState({ 
    Component: null, 
    loading: false, 
    error: null 
  })(({ state, stateUtils }) => {
    const { setState } = stateUtils;
    
    if (!state.Component && !state.loading) {
      setState({ loading: true });
      
      loader()
        .then(module => {
          setState({ 
            Component: module.default || module, 
            loading: false 
          });
        })
        .catch(error => {
          setState({ 
            error: error.message, 
            loading: false 
          });
        });
    }
    
    if (state.error) {
      return {
        div: {
          className: 'lazy-error',
          text: `Failed to load component: ${state.error}`
        }
      };
    }
    
    if (state.loading || !state.Component) {
      return fallback || { div: { text: 'Loading component...' } };
    }
    
    return state.Component(props);
  });
};

// Usage
const App = () => ({
  div: {
    children: [
      LazyComponent({
        loader: () => import('./HeavyComponent.js'),
        fallback: { div: { text: 'Loading heavy component...' } },
        someProp: 'value'
      })
    ]
  }
});

Data-Dependent Components

const DataDependentComponent = ({ dataSource, dependencies = [] }) => {
  return withState({ 
    data: null, 
    loading: false, 
    error: null,
    lastDependencies: null
  })(({ state, stateUtils }) => {
    const { setState } = stateUtils;
    
    const depsChanged = !state.lastDependencies || 
      JSON.stringify(dependencies) !== JSON.stringify(state.lastDependencies);
    
    if (depsChanged && !state.loading) {
      setState({ 
        loading: true, 
        error: null,
        lastDependencies: dependencies
      });
      
      dataSource(dependencies)
        .then(data => setState({ data, loading: false }))
        .catch(error => setState({ error: error.message, loading: false }));
    }
    
    if (state.loading) {
      return { div: { text: 'Loading data...' } };
    }
    
    if (state.error) {
      return { div: { text: `Error: ${state.error}` } };
    }
    
    return {
      div: {
        children: [
          { h3: { text: 'Data-Dependent Component' } },
          state.data ? {
            pre: { text: JSON.stringify(state.data, null, 2) }
          } : { div: { text: 'No data' } }
        ]
      }
    };
  });
};

๐ŸŽจ Style and Theme Management

CSS-in-JS Pattern

const createStyles = (theme) => ({
  container: {
    backgroundColor: theme.colors.background,
    color: theme.colors.text,
    padding: theme.spacing.medium,
    borderRadius: theme.borderRadius,
    fontFamily: theme.fonts.body
  },
  
  button: {
    backgroundColor: theme.colors.primary,
    color: theme.colors.white,
    border: 'none',
    padding: `${theme.spacing.small} ${theme.spacing.medium}`,
    borderRadius: theme.borderRadius,
    cursor: 'pointer',
    fontWeight: 'bold'
  },
  
  input: {
    border: `1px solid ${theme.colors.border}`,
    padding: theme.spacing.small,
    borderRadius: theme.borderRadius,
    fontSize: theme.fonts.size.medium
  }
});

const styleToString = (styleObj) => {
  return Object.entries(styleObj)
    .map(([key, value]) => `${key.replace(/([A-Z])/g, '-\$1').toLowerCase()}: ${value}`)
    .join('; ');
};

const ThemedComponent = withTheme(({ theme, ...props }) => {
  const styles = createStyles(theme);
  
  return {
    div: {
      style: styleToString(styles.container),
      children: [
        {
          input: {
            style: styleToString(styles.input),
            placeholder: 'Enter text...'
          }
        },
        {
          button: {
            style: styleToString(styles.button),
            text: 'Submit'
          }
        }
      ]
    }
  };
});

Responsive Components

const useMediaQuery = (query) => {
  return withState({ matches: false })(({ state, stateUtils }) => {
    const { setState } = stateUtils;
    
    if (typeof window !== 'undefined' && !state._initialized) {
      const mediaQuery = window.matchMedia(query);
      setState({ 
        matches: mediaQuery.matches,
        _initialized: true
      });
      
      const handler = (e) => setState({ matches: e.matches });
      mediaQuery.addEventListener('change', handler);
      
      // Cleanup would need to be handled in a real implementation
    }
    
    return state.matches;
  });
};

const ResponsiveGrid = ({ items }) => {
  const isMobile = useMediaQuery('(max-width: 768px)');
  const isTablet = useMediaQuery('(max-width: 1024px)');
  
  const getColumns = () => {
    if (isMobile) return 1;
    if (isTablet) return 2;
    return 3;
  };
  
  const columns = getColumns();
  
  return {
    div: {
      style: `
        display: grid;
        grid-template-columns: repeat(${columns}, 1fr);
        gap: 1rem;
        padding: 1rem;
      `,
      children: items.map((item, index) => ({
        div: {
          key: index,
          style: `
            border: 1px solid #ccc;
            padding: 1rem;
            border-radius: 4px;
          `,
          children: [
            { h3: { text: item.title } },
            { p: { text: item.description } }
          ]
        }
      }))
    }
  };
};

โšก Performance Optimization

Memoization

const memoizeComponent = (Component, keyExtractor = JSON.stringify) => {
  const cache = new Map();
  
  return (props) => {
    const key = keyExtractor(props);
    
    if (cache.has(key)) {
      return cache.get(key);
    }
    
    const result = Component(props);
    cache.set(key, result);
    
    // Cleanup old entries to prevent memory leaks
    if (cache.size > 100) {
      const firstKey = cache.keys().next().value;
      cache.delete(firstKey);
    }
    
    return result;
  };
};

// Usage
const ExpensiveComponent = memoizeComponent(({ data }) => {
  // Expensive computation
  const processedData = data.map(item => ({
    ...item,
    processed: heavyComputation(item)
  }));
  
  return {
    div: {
      children: processedData.map(item => ({
        div: { key: item.id, text: item.processed }
      }))
    }
  };
});

Virtual Scrolling

const VirtualList = ({ items, itemHeight = 50, visibleCount = 10 }) => {
  return withState({ 
    scrollTop: 0, 
    containerHeight: visibleCount * itemHeight 
  })(({ state, stateUtils }) => {
    const { setState } = stateUtils;
    
    const totalHeight = items.length * itemHeight;
    const startIndex = Math.floor(state.scrollTop / itemHeight);
    const endIndex = Math.min(
      startIndex + visibleCount + 1,
      items.length
    );
    
    const visibleItems = items.slice(startIndex, endIndex);
    const offsetY = startIndex * itemHeight;
    
    return {
      div: {
        style: `
          height: ${state.containerHeight}px;
          overflow-y: auto;
          position: relative;
        `,
        onscroll: (e) => {
          setState({ scrollTop: e.target.scrollTop });
        },
        children: [
          // Spacer for total height
          {
            div: {
              style: `height: ${totalHeight}px; position: relative;`,
              children: [
                // Visible items container
                {
                  div: {
                    style: `
                      position: absolute;
                      top: ${offsetY}px;
                      left: 0;
                      right: 0;
                    `,
                    children: visibleItems.map((item, index) => ({
                      div: {
                        key: startIndex + index,
                        style: `
                          height: ${itemHeight}px;
                          display: flex;
                          align-items: center;
                          padding: 0 1rem;
                          border-bottom: 1px solid #eee;
                        `,
                        children: [
                          { span: { text: item.name } }
                        ]
                      }
                    }))
                  }
                }
              ]
            }
          }
        ]
      }
    };
  });
};

๐Ÿงช Testing Advanced Components

Component Testing Utilities

const createTestRenderer = () => {
  const renders = [];
  
  const mockRenderer = (component) => {
    renders.push(component);
    return JSON.stringify(component);
  };
  
  return {
    render: mockRenderer,
    getRenders: () => renders,
    getLastRender: () => renders[renders.length - 1],
    clearRenders: () => renders.length = 0
  };
};

// Test HOCs
test('withLoading HOC shows loading state', () => {
  const TestComponent = ({ data }) => ({
    div: { text: `Data: ${data}` }
  });
  
  const LoadingTestComponent = withLoading(TestComponent);
  const renderer = createTestRenderer();
  
  // Should show loading initially
  const result = LoadingTestComponent({ data: 'test' });
  expect(result).toMatchObject({
    div: {
      className: 'loading-container'
    }
  });
});

// Test state changes
test('component state updates correctly', () => {
  const stateLogs = [];
  
  const TestComponent = withState({ count: 0 }, { 
    debug: true,
    onStateChange: (oldState, newState) => {
      stateLogs.push({ old: oldState, new: newState });
    }
  })(({ state, stateUtils }) => {
    const { setState } = stateUtils;
    
    return {
      div: {
        children: [
          { span: { text: `Count: ${state.count}` } },
          {
            button: {
              onclick: () => setState({ count: state.count + 1 })
            }
          }
        ]
      }
    };
  });
  
  // Test state change logic
  expect(stateLogs).toHaveLength(1); // Initial state
});

๐Ÿ“š Best Practices for Advanced Components

1. Component Composition Over Inheritance

// โœ… Good - Composition
const EnhancedButton = (props) => BaseButton({
  ...props,
  className: `${props.className} enhanced`,
  children: [
    { span: { className: 'icon' } },
    ...props.children
  ]
});

// โŒ Avoid - Complex inheritance
class ComplexButton extends BaseButton {
  // Complex inheritance hierarchy
}

2. Clear Component Contracts

const Button = ({ 
  text, 
  variant = 'primary', 
  size = 'medium', 
  disabled = false,
  onClick,
  ...props 
}) => {
  // Validate props
  if (!text && !props.children) {
    console.warn('Button requires either text or children');
  }
  
  return {
    button: {
      className: `btn btn--${variant} btn--${size}`,
      disabled,
      onclick: disabled ? undefined : onClick,
      ...props,
      children: text ? [{ span: { text } }] : props.children
    }
  };
};

3. Error Boundaries

const withErrorBoundary = (Component, fallback) => {
  return (props) => {
    try {
      return Component(props);
    } catch (error) {
      console.error('Component error:', error);
      return fallback ? fallback(error, props) : {
        div: {
          className: 'error',
          text: 'Something went wrong'
        }
      };
    }
  };
};

4. Performance Monitoring

const withPerformanceTracking = (Component, componentName) => {
  return (props) => {
    const start = Date.now();
    const result = Component(props);
    const end = Date.now();
    
    if (end - start > 10) {
      console.warn(`Component ${componentName} took ${end - start}ms to render`);
    }
    
    return result;
  };
};

These advanced patterns enable you to build sophisticated, maintainable, and performant applications with Coherent.js. Combine these techniques as needed while keeping components focused and testable.