Juris Component Patterns

Comprehensive guide to component patterns in JurisJS - from basic display components to advanced composition patterns. Learn proven approaches for building maintainable, reusable, and performant UI components.

🧩 Component Basics

Juris components are pure functions that return UI object definitions. They receive props from parents and context for framework interactions.

// Basic component structure
const ComponentName = (props, context) => {
    // Component logic here
    
    return {
        tagName: {
            // Attributes and properties
            className: 'component-class',
            
            // Event handlers
            onClick: (event) => {
                // Handle interactions
            },
            
            // Content
            text: 'Component content',
            
            // Child components
            children: () => [
                { ChildComponent: { prop: 'value' } }
            ]
        }
    };
};

📋 Pattern Categories

🎯 Basic Patterns

Fundamental component patterns for common UI needs

Low Complexity
Use Cases
  • Static content display
  • Simple user interactions
  • Basic conditional rendering

📊 Data Patterns

Components that handle data display, forms, and async operations

Medium Complexity
Use Cases
  • Lists and tables
  • Form validation
  • API integration

🏗️ Composition Patterns

Advanced patterns for component composition and reusability

High Complexity
Use Cases
  • Complex UI layouts
  • Reusable logic
  • Component abstraction

🎯 Simple Display Components

Components that display static or reactive content without complex interactions.

Basic Text Display

// Static text component
const Heading = (props) => ({
    h1: {
        className: `heading ${props.size || 'medium'}`,
        text: props.title,
        style: {
            color: props.color || '#ffffff'
        }
    }
});

// Reactive text component
const UserGreeting = (props, { getState }) => ({
    div: {
        className: 'greeting',
        text: () => {
            const userName = getState('user.name', 'Guest');
            const timeOfDay = new Date().getHours() < 12 ? 'morning' : 'evening';
            return `Good ${timeOfDay}, ${userName}!`;
        }
    }
});

// Status badge component
const StatusBadge = (props, { getState }) => ({
    span: {
        className: () => {
            const status = props.status || getState('system.status', 'unknown');
            return `badge badge-${status}`;
        },
        text: () => (props.status || getState('system.status', 'Unknown')).toUpperCase(),
        style: {
            backgroundColor: () => {
                const status = props.status || getState('system.status');
                const colors = {
                    online: '#22c55e',
                    offline: '#ef4444',
                    maintenance: '#f59e0b'
                };
                return colors[status] || '#6b7280';
            }
        }
    }
});
⚠️ Edge Cases to Handle
  • Null/undefined data: Always provide fallback values
  • Long text overflow: Handle text truncation and tooltips
  • Dynamic styling: Validate color values and CSS properties

Image Display Components

// Responsive image with fallback
const ResponsiveImage = (props) => ({
    div: {
        className: 'image-container',
        children: () => [{
            img: {
                src: props.src,
                alt: props.alt || 'Image',
                className: 'responsive-image',
                onError: (e) => {
                    e.target.src = props.fallback || '/images/placeholder.jpg';
                },
                onLoad: () => {
                    // Image loaded successfully
                    if (props.onLoad) props.onLoad();
                }
            }
        }]
    }
});

// Avatar component with initials fallback
const Avatar = (props, { getState }) => {
    const getInitials = (name) => {
        return name.split(' ').map(n => n[0]).join('').toUpperCase();
    };
    
    return {
        div: {
            className: `avatar ${props.size || 'medium'}`,
            children: () => {
                const user = props.user || getState('user.current');
                if (user?.avatar) {
                    return [{
                        img: {
                            src: user.avatar,
                            alt: user.name,
                            onError: (e) => e.target.style.display = 'none'
                        }
                    }];
                }
                
                return [{
                    span: {
                        className: 'avatar-initials',
                        text: user?.name ? getInitials(user.name) : '?'
                    }
                }];
            }
        }
    };
};

// Icon component with loading state
const Icon = (props) => ({
    i: {
        className: () => {
            const baseClass = 'icon';
            const iconClass = `icon-${props.name}`;
            const sizeClass = props.size ? `icon-${props.size}` : '';
            const loadingClass = props.loading ? 'icon-loading' : '';
            
            return [baseClass, iconClass, sizeClass, loadingClass]
                .filter(Boolean).join(' ');
        },
        style: {
            color: props.color,
            fontSize: props.customSize
        },
        title: props.tooltip
    }
});
⚠️ Edge Cases to Handle
  • Image loading failures: Provide meaningful fallbacks
  • Missing user data: Show placeholder initials or default avatar
  • Icon accessibility: Include proper alt text and ARIA labels

🎛️ Interactive Basic Components

Components that handle user interactions like clicks, form inputs, and state changes.

Button Components

// Basic interactive button
const Button = (props, { getState }) => ({
    button: {
        className: () => {
            const baseClass = 'btn';
            const variantClass = `btn-${props.variant || 'primary'}`;
            const sizeClass = props.size ? `btn-${props.size}` : '';
            const disabledClass = props.disabled || getState('ui.loading') ? 'btn-disabled' : '';
            
            return [baseClass, variantClass, sizeClass, disabledClass]
                .filter(Boolean).join(' ');
        },
        disabled: () => props.disabled || getState('ui.loading'),
        onClick: (e) => {
            if (props.disabled || getState('ui.loading')) return;
            
            if (props.onClick) {
                props.onClick(e);
            }
        },
        children: () => {
            if (getState('ui.loading') && props.showLoading) {
                return [
                    { Icon: { name: 'spinner', className: 'btn-spinner' } },
                    { span: { text: 'Loading...' } }
                ];
            }
            
            return [
                props.icon ? { Icon: { name: props.icon } } : null,
                { span: { text: props.text || props.children } }
            ].filter(Boolean);
        }
    }
});

// Toggle button with state
const ToggleButton = (props, { getState, setState }) => {
    const isActive = () => getState(props.statePath, props.defaultValue || false);
    
    return {
        button: {
            className: () => `toggle-btn ${isActive() ? 'active' : 'inactive'}`,
            onClick: () => {
                const newValue = !isActive();
                setState(props.statePath, newValue);
                
                if (props.onChange) {
                    props.onChange(newValue);
                }
            },
            children: () => [{
                span: {
                    text: isActive() ? props.activeText : props.inactiveText
                }
            }]
        }
    };
};

// Action button with confirmation
const ActionButton = (props, { setState }) => ({
    button: {
        className: `action-btn ${props.danger ? 'btn-danger' : 'btn-primary'}`,
        onClick: async (e) => {
            if (props.requireConfirmation) {
                const confirmed = confirm(props.confirmMessage || 'Are you sure?');
                if (!confirmed) return;
            }
            
            if (props.async) {
                setState('ui.loading', true);
                try {
                    await props.onClick(e);
                } catch (error) {
                    setState('ui.error', error.message);
                } finally {
                    setState('ui.loading', false);
                }
            } else {
                props.onClick(e);
            }
        },
        text: props.text
    }
});
⚠️ Edge Cases to Handle
  • Double-click prevention: Disable button during async operations
  • Keyboard accessibility: Handle Enter and Space key events
  • Loading states: Show visual feedback during async actions

Input Components

// Controlled input with validation
const TextInput = (props, { useState }) => {
  const [value, setValue] = useState(props.statePath, props.defaultValue || '');
  const [error, setError] = useState(`${props.statePath}.error`, null);
  
  const validate = (newValue) => {
      if (props.required && !newValue.trim()) {
          return 'This field is required';
      }
      return null;
  };
  
  return {
      div: {
          className: 'input-group',
          children: () => [
              {
                  input: {
                      value: value(),
                      onInput: (e) => {
                          const newValue = e.target.value;
                          setValue(newValue);
                          setError(null); // Clear error on input
                      },
                      onBlur: (e) => {
                          const validationError = validate(e.target.value);
                          setError(validationError);
                      }
                  }
              }
          ]
      }
  };
};

// Select dropdown component
const Select = (props, { getState, setState }) => {
    const getValue = () => getState(props.statePath, props.defaultValue || '');
    
    return {
        div: {
            className: 'select-group',
            children: () => [
                props.label ? {
                    label: {
                        text: props.label,
                        className: 'select-label'
                    }
                } : null,
                {
                    select: {
                        value: () => getValue(),
                        className: 'form-select',
                        onChange: (e) => {
                            const value = e.target.value;
                            setState(props.statePath, value);
                            
                            if (props.onChange) {
                                props.onChange(value);
                            }
                        },
                        children: () => [
                            props.placeholder ? {
                                option: {
                                    value: '',
                                    text: props.placeholder,
                                    disabled: true
                                }
                            } : null,
                            ...props.options.map(option => ({
                                option: {
                                    value: option.value,
                                    text: option.label || option.value,
                                    selected: () => getValue() === option.value
                                }
                            }))
                        ].filter(Boolean)
                    }
                }
            ].filter(Boolean)
        }
    };
};

// Checkbox with indeterminate state
const Checkbox = (props, { getState, setState }) => {
    const isChecked = () => getState(props.statePath, props.defaultValue || false);
    
    return {
        label: {
            className: 'checkbox-label',
            children: () => [
                {
                    input: {
                        type: 'checkbox',
                        checked: () => isChecked(),
                        indeterminate: () => props.indeterminate,
                        onChange: (e) => {
                            const checked = e.target.checked;
                            setState(props.statePath, checked);
                            
                            if (props.onChange) {
                                props.onChange(checked);
                            }
                        }
                    }
                },
                {
                    span: {
                        text: props.label,
                        className: 'checkbox-text'
                    }
                }
            ]
        }
    };
};
⚠️ Edge Cases to Handle
  • Input sanitization: Clean and validate user input
  • Real-time validation: Balance UX with validation feedback timing
  • Accessibility: Proper labeling and ARIA attributes

🔀 Conditional Rendering Patterns

Components that render different content based on state, props, or conditions.

Conditional Display

// Simple conditional rendering
const ConditionalMessage = (props, { getState }) => ({
    div: {
        children: () => {
            const isLoggedIn = getState('auth.isAuthenticated', false);
            
            if (isLoggedIn) {
                return [{
                    div: {
                        className: 'welcome-message',
                        text: `Welcome back, ${getState('auth.user.name', 'User')}!`
                    }
                }];
            }
            
            return [{
                div: {
                    className: 'login-prompt',
                    children: [
                        { p: { text: 'Please log in to continue' } },
                        { Button: { text: 'Login', onClick: props.onLogin } }
                    ]
                }
            }];
        }
    }
});

// Multi-state conditional rendering
const LoadingStateComponent = (props, { getState }) => ({
    div: {
        className: 'state-container',
        children: () => {
            const loading = getState('ui.loading', false);
            const error = getState('ui.error', null);
            const data = getState('data.items', null);
            
            // Loading state
            if (loading) {
                return [{
                    div: {
                        className: 'loading-state',
                        children: [
                            { Icon: { name: 'spinner', className: 'spinner' } },
                            { p: { text: 'Loading...' } }
                        ]
                    }
                }];
            }
            
            // Error state
            if (error) {
                return [{
                    div: {
                        className: 'error-state',
                        children: [
                            { Icon: { name: 'alert-circle', className: 'error-icon' } },
                            { p: { text: error } },
                            { Button: { 
                                text: 'Retry', 
                                onClick: props.onRetry,
                                variant: 'secondary'
                            }}
                        ]
                    }
                }];
            }
            
            // Empty state
            if (!data || data.length === 0) {
                return [{
                    div: {
                        className: 'empty-state',
                        children: [
                            { Icon: { name: 'inbox', className: 'empty-icon' } },
                            { p: { text: 'No items found' } },
                            { Button: { 
                                text: 'Add Item', 
                                onClick: props.onAdd 
                            }}
                        ]
                    }
                }];
            }
            
            // Success state with data
            return [{
                div: {
                    className: 'data-state',
                    children: data.map(item => ({
                        DataItem: { item }
                    }))
                }
            }];
        }
    }
});

// Permission-based rendering
const PermissionGate = (props, { getState }) => {
    const hasPermission = () => {
        const userRole = getState('auth.user.role', 'guest');
        const userPermissions = getState('auth.user.permissions', []);
        
        // Check role-based permissions
        if (props.requiredRole && userRole !== props.requiredRole) {
            return false;
        }
        
        // Check specific permissions
        if (props.requiredPermissions) {
            return props.requiredPermissions.every(permission => 
                userPermissions.includes(permission)
            );
        }
        
        return true;
    };
    
    return {
        div: {
            children: () => {
                if (!hasPermission()) {
                    if (props.fallback) {
                        return [props.fallback];
                    }
                    
                    return [{
                        div: {
                            className: 'permission-denied',
                            text: 'Access denied'
                        }
                    }];
                }
                
                return props.children || [];
            }
        }
    };
};
⚠️ Edge Cases to Handle
  • Race conditions: Handle rapid state changes gracefully
  • Permission checks: Validate permissions on both client and server
  • Fallback content: Always provide meaningful fallbacks

📋 List Rendering Patterns

Components for displaying dynamic lists, tables, and collections of data.

Dynamic Lists

// Basic list component
const ItemList = (props, { getState }) => ({
    div: {
        className: 'item-list',
        children: () => {
            const items = getState(props.dataPath, []);
            
            if (items.length === 0) {
                return [{
                    div: {
                        className: 'empty-list',
                        text: props.emptyMessage || 'No items available'
                    }
                }];
            }
            
            return items.map((item, index) => ({
                div: {
                    key: item.id || index,
                    className: 'list-item',
                    children: [{
                        [props.itemComponent]: {
                            item,
                            index,
                            onUpdate: props.onItemUpdate,
                            onDelete: props.onItemDelete
                        }
                    }]
                }
            }));
        }
    }
});

// Filterable and sortable list
const FilterableList = (props, { getState, setState }) => {
    const getFilteredItems = () => {
        let items = getState(props.dataPath, []);
        const filter = getState('ui.filter', '');
        const sortBy = getState('ui.sortBy', 'name');
        const sortOrder = getState('ui.sortOrder', 'asc');
        
        // Apply filter
        if (filter) {
            items = items.filter(item => 
                Object.values(item)
                    .join(' ')
                    .toLowerCase()
                    .includes(filter.toLowerCase())
            );
        }
        
        // Apply sorting
        items.sort((a, b) => {
            const aVal = a[sortBy] || '';
            const bVal = b[sortBy] || '';
            const comparison = aVal.toString().localeCompare(bVal.toString());
            return sortOrder === 'desc' ? -comparison : comparison;
        });
        
        return items;
    };
    
    return {
        div: {
            className: 'filterable-list',
            children: () => [
                {
                    div: {
                        className: 'list-controls',
                        children: [
                            {
                                TextInput: {
                                    statePath: 'ui.filter',
                                    placeholder: 'Search items...',
                                    className: 'filter-input'
                                }
                            },
                            {
                                Select: {
                                    statePath: 'ui.sortBy',
                                    options: props.sortOptions || [
                                        { value: 'name', label: 'Name' },
                                        { value: 'date', label: 'Date' }
                                    ]
                                }
                            }
                        ]
                    }
                },
                {
                    div: {
                        className: 'filtered-items',
                        children: () => getFilteredItems().map(item => ({
                            [props.itemComponent]: { item }
                        }))
                    }
                }
            ]
        }
    };
};

// Paginated list component
const PaginatedList = (props, { getState, setState }) => {
    const pageSize = props.pageSize || 10;
    const currentPage = () => getState('ui.currentPage', 1);
    const items = () => getState(props.dataPath, []);
    
    const totalPages = () => Math.ceil(items().length / pageSize);
    const startIndex = () => (currentPage() - 1) * pageSize;
    const endIndex = () => startIndex() + pageSize;
    const currentItems = () => items().slice(startIndex(), endIndex());
    
    return {
        div: {
            className: 'paginated-list',
            children: () => [
                {
                    div: {
                        className: 'pagination-info',
                        text: () => {
                            const total = items().length;
                            const start = startIndex() + 1;
                            const end = Math.min(endIndex(), total);
                            return `Showing ${start}-${end} of ${total} items`;
                        }
                    }
                },
                {
                    div: {
                        className: 'paginated-items',
                        children: () => currentItems().map(item => ({
                            [props.itemComponent]: { item }
                        }))
                    }
                },
                {
                    div: {
                        className: 'pagination-controls',
                        children: () => [
                            {
                                Button: {
                                    text: 'Previous',
                                    disabled: () => currentPage() <= 1,
                                    onClick: () => setState('ui.currentPage', currentPage() - 1)
                                }
                            },
                            {
                                span: {
                                    className: 'page-info',
                                    text: () => `Page ${currentPage()} of ${totalPages()}`
                                }
                            },
                            {
                                Button: {
                                    text: 'Next',
                                    disabled: () => currentPage() >= totalPages(),
                                    onClick: () => setState('ui.currentPage', currentPage() + 1)
                                }
                            }
                        ]
                    }
                }
            ]
        }
    };
};
⚠️ Edge Cases to Handle
  • Large datasets: Implement virtual scrolling for performance
  • Real-time updates: Handle list changes while user is interacting
  • Selection state: Maintain selection across pagination and filtering

📝 Form Handling Patterns

Comprehensive form components with validation, submission, and error handling.

Form Components

// Complete form component with validation
const ContactForm = (props, { getState, setState }) => {
    const formData = () => getState('form.contact', {
        name: '',
        email: '',
        message: ''
    });
    
    const errors = () => getState('form.contact.errors', {});
    const isSubmitting = () => getState('form.contact.submitting', false);
    
    const validateField = (field, value) => {
        const validations = {
            name: (val) => {
                if (!val.trim()) return 'Name is required';
                if (val.length < 2) return 'Name must be at least 2 characters';
                return null;
            },
            email: (val) => {
                if (!val.trim()) return 'Email is required';
                const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
                if (!emailRegex.test(val)) return 'Please enter a valid email';
                return null;
            },
            message: (val) => {
                if (!val.trim()) return 'Message is required';
                if (val.length < 10) return 'Message must be at least 10 characters';
                return null;
            }
        };
        
        return validations[field] ? validations[field](value) : null;
    };
    
    const validateForm = () => {
        const data = formData();
        const newErrors = {};
        
        Object.keys(data).forEach(field => {
            const error = validateField(field, data[field]);
            if (error) newErrors[field] = error;
        });
        
        setState('form.contact.errors', newErrors);
        return Object.keys(newErrors).length === 0;
    };
    
    const handleSubmit = async (e) => {
        e.preventDefault();
        
        if (!validateForm()) return;
        
        setState('form.contact.submitting', true);
        
        try {
            await props.onSubmit(formData());
            setState('form.contact', { name: '', email: '', message: '' });
            setState('form.contact.errors', {});
            setState('ui.notification', {
                type: 'success',
                message: 'Message sent successfully!'
            });
        } catch (error) {
            setState('form.contact.errors', {
                submit: 'Failed to send message. Please try again.'
            });
        } finally {
            setState('form.contact.submitting', false);
        }
    };
    
    return {
        form: {
            className: 'contact-form',
            onSubmit: handleSubmit,
            children: () => [
                {
                    div: {
                        className: 'form-row',
                        children: [
                            {
                                TextInput: {
                                    label: 'Name',
                                    statePath: 'form.contact.name',
                                    required: true,
                                    error: () => errors().name
                                }
                            }
                        ]
                    }
                },
                {
                    div: {
                        className: 'form-row',
                        children: [
                            {
                                TextInput: {
                                    label: 'Email',
                                    type: 'email',
                                    statePath: 'form.contact.email',
                                    required: true,
                                    error: () => errors().email
                                }
                            }
                        ]
                    }
                },
                {
                    div: {
                        className: 'form-row',
                        children: [
                            {
                                textarea: {
                                    placeholder: 'Your message...',
                                    value: () => formData().message,
                                    className: () => errors().message ? 'error' : '',
                                    onInput: (e) => {
                                        setState('form.contact.message', e.target.value);
                                        // Clear error on input
                                        if (errors().message) {
                                            const newErrors = { ...errors() };
                                            delete newErrors.message;
                                            setState('form.contact.errors', newErrors);
                                        }
                                    }
                                }
                            }
                        ]
                    }
                },
                errors().submit ? {
                    div: {
                        className: 'form-error',
                        text: () => errors().submit
                    }
                } : null,
                {
                    div: {
                        className: 'form-actions',
                        children: [
                            {
                                Button: {
                                    type: 'submit',
                                    text: () => isSubmitting() ? 'Sending...' : 'Send Message',
                                    disabled: () => isSubmitting(),
                                    variant: 'primary'
                                }
                            }
                        ]
                    }
                }
            ].filter(Boolean)
        }
    };
};

// Multi-step form component
const MultiStepForm = (props, { getState, setState }) => {
    const currentStep = () => getState('form.multiStep.currentStep', 1);
    const totalSteps = props.steps.length;
    
    const nextStep = () => {
        if (currentStep() < totalSteps) {
            setState('form.multiStep.currentStep', currentStep() + 1);
        }
    };
    
    const prevStep = () => {
        if (currentStep() > 1) {
            setState('form.multiStep.currentStep', currentStep() - 1);
        }
    };
    
    const getCurrentStepComponent = () => {
        const step = props.steps[currentStep() - 1];
        return step ? step.component : null;
    };
    
    return {
        div: {
            className: 'multi-step-form',
            children: () => [
                {
                    div: {
                        className: 'step-indicator',
                        children: () => props.steps.map((step, index) => ({
                            div: {
                                className: () => {
                                    const stepNumber = index + 1;
                                    const classes = ['step'];
                                    if (stepNumber === currentStep()) classes.push('active');
                                    if (stepNumber < currentStep()) classes.push('completed');
                                    return classes.join(' ');
                                },
                                children: [
                                    {
                                        span: {
                                            className: 'step-number',
                                            text: index + 1
                                        }
                                    },
                                    {
                                        span: {
                                            className: 'step-title',
                                            text: step.title
                                        }
                                    }
                                ]
                            }
                        }))
                    }
                },
                {
                    div: {
                        className: 'step-content',
                        children: () => {
                            const StepComponent = getCurrentStepComponent();
                            return StepComponent ? [{ [StepComponent]: {} }] : [];
                        }
                    }
                },
                {
                    div: {
                        className: 'step-navigation',
                        children: () => [
                            {
                                Button: {
                                    text: 'Previous',
                                    disabled: () => currentStep() <= 1,
                                    onClick: prevStep,
                                    variant: 'secondary'
                                }
                            },
                            {
                                Button: {
                                    text: () => currentStep() === totalSteps ? 'Submit' : 'Next',
                                    onClick: () => {
                                        if (currentStep() === totalSteps) {
                                            props.onSubmit();
                                        } else {
                                            nextStep();
                                        }
                                    },
                                    variant: 'primary'
                                }
                            }
                        ]
                    }
                }
            ]
        }
    };
};

// Form field with dynamic validation
const ValidatedField = (props, { getState, setState }) => {
    const value = () => getState(props.statePath, '');
    const error = () => getState(`${props.statePath}.error`, null);
    const touched = () => getState(`${props.statePath}.touched`, false);
    
    const runValidation = async (val) => {
        if (props.validators) {
            for (const validator of props.validators) {
                const result = await validator(val);
                if (result) {
                    setState(`${props.statePath}.error`, result);
                    return result;
                }
            }
        }
        
        setState(`${props.statePath}.error`, null);
        return null;
    };
    
    return {
        div: {
            className: 'validated-field',
            children: () => [
                {
                    input: {
                        type: props.type || 'text',
                        value: () => value(),
                        placeholder: props.placeholder,
                        className: () => {
                            const classes = ['form-input'];
                            if (touched() && error()) classes.push('error');
                            if (touched() && !error()) classes.push('valid');
                            return classes.join(' ');
                        },
                        onInput: (e) => {
                            setState(props.statePath, e.target.value);
                            runValidation(e.target.value);
                        },
                        onBlur: () => {
                            setState(`${props.statePath}.touched`, true);
                            runValidation(value());
                        }
                    }
                },
                touched() && error() ? {
                    div: {
                        className: 'field-error',
                        text: () => error()
                    }
                } : null
            ].filter(Boolean)
        }
    };
};
⚠️ Edge Cases to Handle
  • Async validation: Handle server-side validation and debouncing
  • Form persistence: Save draft data during navigation
  • File uploads: Handle progress, validation, and error states

🔄 Async Data Patterns

Components that handle asynchronous operations, API calls, and data loading states.

Data Loading Components

// Async data loader with caching
const DataLoader = (props, { getState, setState }) => {
    const cacheKey = props.cacheKey || props.url;
    const data = () => getState(`cache.${cacheKey}`, null);
    const loading = () => getState(`loading.${cacheKey}`, false);
    const error = () => getState(`errors.${cacheKey}`, null);
    const lastFetch = () => getState(`lastFetch.${cacheKey}`, 0);
    
    const shouldRefetch = () => {
        const maxAge = props.maxAge || 300000; // 5 minutes
        return Date.now() - lastFetch() > maxAge;
    };
    
    const fetchData = async () => {
        if (loading()) return;
        
        setState(`loading.${cacheKey}`, true);
        setState(`errors.${cacheKey}`, null);
        
        try {
            const response = await fetch(props.url, props.options || {});
            
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }
            
            const result = await response.json();
            setState(`cache.${cacheKey}`, result);
            setState(`lastFetch.${cacheKey}`, Date.now());
            
            if (props.onSuccess) {
                props.onSuccess(result);
            }
        } catch (err) {
            setState(`errors.${cacheKey}`, err.message);
            
            if (props.onError) {
                props.onError(err);
            }
        } finally {
            setState(`loading.${cacheKey}`, false);
        }
    };
    
    return {
        div: {
            className: 'data-loader',
            onMount: () => {
                if (!data() || shouldRefetch()) {
                    fetchData();
                }
            },
            children: () => {
                if (loading() && !data()) {
                    return [{
                        div: {
                            className: 'loading-state',
                            children: [
                                { Icon: { name: 'spinner' } },
                                { span: { text: props.loadingText || 'Loading...' } }
                            ]
                        }
                    }];
                }
                
                if (error() && !data()) {
                    return [{
                        div: {
                            className: 'error-state',
                            children: [
                                { p: { text: error() } },
                                { Button: { 
                                    text: 'Retry', 
                                    onClick: fetchData 
                                }}
                            ]
                        }
                    }];
                }
                
                if (data()) {
                    return [{
                        div: {
                            className: 'data-content',
                            children: [
                                loading() ? {
                                    div: {
                                        className: 'refresh-indicator',
                                        text: 'Refreshing...'
                                    }
                                } : null,
                                { [props.component]: { data: data() } }
                            ].filter(Boolean)
                        }
                    }];
                }
                
                return [];
            }
        }
    };
};

// Infinite scroll data loader
const InfiniteLoader = (props, { getState, setState }) => {
    const items = () => getState(props.dataPath, []);
    const loading = () => getState('ui.infiniteLoading', false);
    const hasMore = () => getState('ui.hasMore', true);
    const page = () => getState('ui.currentPage', 1);
    
    const loadMore = async () => {
        if (loading() || !hasMore()) return;
        
        setState('ui.infiniteLoading', true);
        
        try {
            const response = await fetch(`${props.url}?page=${page()}&limit=${props.pageSize || 20}`);
            const data = await response.json();
            
            const newItems = [...items(), ...data.items];
            setState(props.dataPath, newItems);
            setState('ui.currentPage', page() + 1);
            setState('ui.hasMore', data.hasMore);
            
        } catch (error) {
            setState('ui.error', 'Failed to load more items');
        } finally {
            setState('ui.infiniteLoading', false);
        }
    };
    
    return {
        div: {
            className: 'infinite-loader',
            children: () => [
                {
                    div: {
                        className: 'infinite-content',
                        children: () => items().map(item => ({
                            [props.itemComponent]: { item }
                        }))
                    }
                },
                hasMore() ? {
                    div: {
                        className: 'load-trigger',
                        onIntersect: loadMore, // Custom intersection observer
                        children: [{
                            Button: {
                                text: () => loading() ? 'Loading...' : 'Load More',
                                disabled: () => loading(),
                                onClick: loadMore
                            }
                        }]
                    }
                } : {
                    div: {
                        className: 'end-indicator',
                        text: 'No more items to load'
                    }
                }
            ]
        }
    };
};

// Real-time data component with WebSocket
const RealTimeData = (props, context) => {
  return {
      render: () => ({
          div: {
              className: 'realtime-data',
              children: () => [
                  {
                      div: {
                          className: () => {
                              const connected = context.getState('ws.connected', false);
                              return `connection-status ${connected ? 'connected' : 'disconnected'}`;
                          },
                          children: () => {
                              const connected = context.getState('ws.connected', false);
                              const error = context.getState('ws.error', null);
                              
                              if (error) {
                                  return [
                                      { Icon: { name: 'alert-circle', className: 'status-icon' } },
                                      { span: { text: `Connection Error: ${error}` } }
                                  ];
                              }
                              
                              return [
                                  { Icon: { 
                                      name: connected ? 'wifi' : 'wifi-off',
                                      className: 'status-icon'
                                  }},
                                  { span: { 
                                      text: connected ? 'Connected' : 'Disconnected',
                                      className: 'status-text'
                                  }}
                              ];
                          }
                      }
                  },
                  {
                      div: {
                          className: 'data-content',
                          children: () => {
                              const data = context.getState(props.dataPath);
                              
                              if (!data) {
                                  return [{ div: { 
                                      className: 'no-data',
                                      text: 'Waiting for data...' 
                                  }}];
                              }
                              
                              // Render data using provided component or default
                              if (props.component) {
                                  return [{ [props.component]: { data } }];
                              }
                              
                              // Default data display
                              return [{ 
                                  pre: { 
                                      text: JSON.stringify(data, null, 2),
                                      className: 'data-display'
                                  }
                              }];
                          }
                      }
                  }
              ]
          }
      }),

      onMount: (element, componentProps, componentContext) => {
          let socket = null;
          let reconnectTimeout = null;
          let reconnectAttempts = 0;
          
          const maxReconnectAttempts = componentProps.maxReconnectAttempts || 5;
          const reconnectDelay = componentProps.reconnectDelay || 3000;
          const autoReconnect = componentProps.autoReconnect !== false;
          
          const connect = () => {
              if (socket && socket.readyState === WebSocket.OPEN) {
                  return; // Already connected
              }
              
              console.log(`🔄 Connecting to WebSocket: ${componentProps.wsUrl}`);
              componentContext.setState('ws.connecting', true);
              
              try {
                  socket = new WebSocket(componentProps.wsUrl);
                  
                  socket.onopen = () => {
                      console.log('✅ WebSocket connected');
                      componentContext.setState('ws.connected', true);
                      componentContext.setState('ws.connecting', false);
                      componentContext.setState('ws.error', null);
                      reconnectAttempts = 0; // Reset on successful connection
                      
                      // Call onConnect callback if provided
                      if (componentProps.onConnect) {
                          componentProps.onConnect(socket);
                      }
                  };
                  
                  socket.onmessage = (event) => {
                      try {
                          const data = JSON.parse(event.data);
                          
                          // Call custom message handler if provided
                          if (componentProps.onMessage) {
                              componentProps.onMessage(data, componentContext);
                          } else {
                              // Default: update the specified data path
                              componentContext.setState(componentProps.dataPath, data);
                          }
                          
                          // Update last received timestamp
                          componentContext.setState('ws.lastReceived', Date.now());
                          
                      } catch (error) {
                          console.error('❌ WebSocket message parse error:', error);
                          componentContext.setState('ws.error', 'Invalid message format');
                      }
                  };
                  
                  socket.onerror = (error) => {
                      console.error('❌ WebSocket error:', error);
                      componentContext.setState('ws.error', 'Connection failed');
                      componentContext.setState('ws.connecting', false);
                      
                      if (componentProps.onError) {
                          componentProps.onError(error, componentContext);
                      }
                  };
                  
                  socket.onclose = (event) => {
                      console.log(`🔌 WebSocket closed: ${event.code} ${event.reason}`);
                      componentContext.setState('ws.connected', false);
                      componentContext.setState('ws.connecting', false);
                      
                      socket = null;
                      
                      // Call onDisconnect callback if provided
                      if (componentProps.onDisconnect) {
                          componentProps.onDisconnect(event, componentContext);
                      }
                      
                      // Auto-reconnect logic
                      if (autoReconnect && reconnectAttempts < maxReconnectAttempts) {
                          reconnectAttempts++;
                          const delay = reconnectDelay * Math.pow(1.5, reconnectAttempts - 1); // Exponential backoff
                          
                          console.log(`🔄 Reconnecting in ${delay}ms (attempt ${reconnectAttempts}/${maxReconnectAttempts})`);
                          componentContext.setState('ws.reconnecting', true);
                          
                          reconnectTimeout = setTimeout(() => {
                              componentContext.setState('ws.reconnecting', false);
                              connect();
                          }, delay);
                      } else if (reconnectAttempts >= maxReconnectAttempts) {
                          console.error('❌ Max reconnection attempts reached');
                          componentContext.setState('ws.error', 'Connection lost - max retries exceeded');
                      }
                  };
                  
              } catch (error) {
                  console.error('❌ WebSocket connection error:', error);
                  componentContext.setState('ws.error', error.message);
                  componentContext.setState('ws.connecting', false);
              }
          };
          
          const disconnect = () => {
              if (reconnectTimeout) {
                  clearTimeout(reconnectTimeout);
                  reconnectTimeout = null;
              }
              
              if (socket) {
                  socket.close(1000, 'Component unmounting');
                  socket = null;
              }
              
              // Clear all WebSocket state
              componentContext.setState('ws.connected', false);
              componentContext.setState('ws.connecting', false);
              componentContext.setState('ws.reconnecting', false);
              componentContext.setState('ws.error', null);
          };
          
          // Initial connection
          connect();
          
          // Expose methods to component API (optional)
          if (componentProps.exposeAPI) {
              element._wsAPI = {
                  connect,
                  disconnect,
                  send: (data) => {
                      if (socket && socket.readyState === WebSocket.OPEN) {
                          socket.send(JSON.stringify(data));
                          return true;
                      }
                      return false;
                  },
                  getStatus: () => ({
                      connected: componentContext.getState('ws.connected', false),
                      connecting: componentContext.getState('ws.connecting', false),
                      reconnecting: componentContext.getState('ws.reconnecting', false),
                      error: componentContext.getState('ws.error', null),
                      attempts: reconnectAttempts
                  })
              };
          }
          
          // Return cleanup function - this is called on component unmount
          return () => {
              console.log('🧹 Cleaning up WebSocket connection');
              disconnect();
          };
      },

      onError: (error, element, props, context) => {
          console.error('❌ RealTimeData component error:', error);
          context.setState('ws.error', `Component error: ${error.message}`);
          
          // Attempt to recover by reconnecting
          if (props.recoverOnError !== false) {
              setTimeout(() => {
                  context.setState('ws.error', null);
                  // Force remount by updating a dummy state
                  context.setState('ws.forceReconnect', Date.now());
              }, 2000);
          }
      }
  };
};

const RealTimeDataSimple = (props, context) => {
  return {
      render: () => {
          const [connected, setConnected] = context.useState('ws.connected', false);
          const [error, setError] = context.useState('ws.error', null);
          const [data, setData] = context.useState(props.dataPath, null);
          
          return {
              div: {
                  className: 'realtime-data-simple',
                  children: () => [
                      // Connection status
                      {
                          div: {
                              className: `status ${connected() ? 'connected' : 'disconnected'}`,
                              text: () => {
                                  if (error()) return `Error: ${error()}`;
                                  return connected() ? '🟢 Connected' : '🔴 Disconnected';
                              }
                          }
                      },
                      // Data display
                      {
                          div: {
                              className: 'data',
                              children: () => {
                                  const currentData = data();
                                  if (!currentData) {
                                      return [{ div: { text: 'No data received yet...' } }];
                                  }
                                  
                                  return props.renderData 
                                      ? [props.renderData(currentData)]
                                      : [{ pre: { text: JSON.stringify(currentData, null, 2) } }];
                              }
                          }
                      }
                  ]
              }
          };
      },

      onMount: (element, componentProps, componentContext) => {
          const [, setConnected] = componentContext.useState('ws.connected', false);
          const [, setError] = componentContext.useState('ws.error', null);
          const [, setData] = componentContext.useState(componentProps.dataPath, null);
          
          const socket = new WebSocket(componentProps.wsUrl);
          
          socket.onopen = () => {
              setConnected(true);
              setError(null);
          };
          
          socket.onmessage = (event) => {
              try {
                  const data = JSON.parse(event.data);
                  setData(data);
              } catch (err) {
                  setError('Invalid data format');
              }
          };
          
          socket.onerror = () => {
              setError('Connection failed');
              setConnected(false);
          };
          
          socket.onclose = () => {
              setConnected(false);
          };
          
          // Cleanup function
          return () => {
              socket.close();
          };
      }
  };
};

// Usage examples:
const examples = {
    basic: {
        RealTimeData: {
            wsUrl: 'wss://api.example.com/live',
            dataPath: 'liveData.prices',
            component: 'PriceDisplay'
        }
    },
    
    advanced: {
        RealTimeData: {
            wsUrl: 'wss://api.example.com/live',
            dataPath: 'liveData.messages',
            maxReconnectAttempts: 10,
            reconnectDelay: 2000,
            autoReconnect: true,
            exposeAPI: true,
            onConnect: (socket) => {
                socket.send(JSON.stringify({ action: 'subscribe', channel: 'notifications' }));
            },
            onMessage: (data, context) => {
                // Custom message handling
                if (data.type === 'notification') {
                    context.setState('notifications', data.payload);
                } else if (data.type === 'update') {
                    context.setState('liveData.updates', data.payload);
                }
            },
            onError: (error, context) => {
                context.setState('ui.toast', {
                    type: 'error',
                    message: 'Real-time connection lost'
                });
            }
        }
    },
    
    simple: {
        RealTimeDataSimple: {
            wsUrl: 'wss://api.example.com/simple',
            dataPath: 'simpleData',
            renderData: (data) => ({
                div: {
                    className: 'custom-data-display',
                    text: `Latest: ${data.value} at ${new Date(data.timestamp).toLocaleTimeString()}`
                }
            })
        }
    }
};

⚠️ Edge Cases to Handle
  • Network failures: Implement retry logic with exponential backoff
  • Race conditions: Cancel previous requests when new ones are made
  • Memory leaks: Clean up subscriptions and intervals on unmount

👨‍👧‍👦 Parent-Child Communication

Patterns for data flow and communication between parent and child components.

Props and Callbacks

// Parent component managing child state
const TodoApp = (props, { getState, setState }) => {
    const todos = () => getState('todos', []);
    
    const addTodo = (text) => {
        const newTodo = {
            id: Date.now(),
            text: text.trim(),
            completed: false,
            createdAt: new Date()
        };
        
        setState('todos', [...todos(), newTodo]);
    };
    
    const updateTodo = (id, updates) => {
        const updated = todos().map(todo => 
            todo.id === id ? { ...todo, ...updates } : todo
        );
        setState('todos', updated);
    };
    
    const deleteTodo = (id) => {
        const filtered = todos().filter(todo => todo.id !== id);
        setState('todos', filtered);
    };
    
    return {
        div: {
            className: 'todo-app',
            children: () => [
                {
                    TodoForm: {
                        onSubmit: addTodo,
                        placeholder: 'Add a new todo...'
                    }
                },
                {
                    TodoList: {
                        items: todos(),
                        onUpdate: updateTodo,
                        onDelete: deleteTodo
                    }
                },
                {
                    TodoStats: {
                        total: todos().length,
                        completed: todos().filter(t => t.completed).length
                    }
                }
            ]
        }
    };
};

// Child component receiving props and callbacks
const TodoItem = (props) => ({
    div: {
        className: () => `todo-item ${props.item.completed ? 'completed' : ''}`,
        children: () => [
            {
                Checkbox: {
                    checked: props.item.completed,
                    onChange: (checked) => {
                        props.onUpdate(props.item.id, { completed: checked });
                    }
                }
            },
            {
                span: {
                    className: 'todo-text',
                    text: props.item.text,
                    onDoubleClick: () => {
                        // Enable inline editing
                        props.onEdit(props.item.id);
                    }
                }
            },
            {
                Button: {
                    text: 'Delete',
                    variant: 'danger',
                    size: 'small',
                    onClick: () => {
                        if (confirm('Delete this todo?')) {
                            props.onDelete(props.item.id);
                        }
                    }
                }
            }
        ]
    }
});

// Provider pattern for deep prop passing
const ThemeProvider = (props, { getState, setState }) => {
    const theme = () => getState('ui.theme', 'light');
    
    const toggleTheme = () => {
        const newTheme = theme() === 'light' ? 'dark' : 'light';
        setState('ui.theme', newTheme);
    };
    
    const themeContext = {
        theme: theme(),
        toggleTheme,
        colors: {
            light: { bg: '#ffffff', text: '#000000' },
            dark: { bg: '#1a1a1a', text: '#ffffff' }
        }
    };
    
    return {
        div: {
            className: () => `theme-provider theme-${theme()}`,
            style: () => ({
                backgroundColor: themeContext.colors[theme()].bg,
                color: themeContext.colors[theme()].text
            }),
            children: () => props.children.map(child => ({
                ...child,
                themeContext
            }))
        }
    };
};
⚠️ Edge Cases to Handle
  • Prop drilling: Use context or state management for deep hierarchies
  • Callback stability: Ensure callbacks don't cause unnecessary re-renders
  • Data synchronization: Handle conflicts when multiple children update shared state

🎭 Higher-Order Components

Patterns for component composition and cross-cutting concerns.

HOC Patterns

// Authentication HOC
  const withAuth = (WrappedComponent) => {
    return (props, context) => {
        const { getState, navigate } = context;
        const isAuthenticated = () => getState('auth.isAuthenticated', false);
        
        return {
            div: {
                children: () => {
                    if (!isAuthenticated()) {
                        return [{
                            div: {
                                className: 'auth-required',
                                text: 'Please log in to access this content'
                            }
                        }];
                    }
                    
                    // Render wrapped component by returning its definition
                    return [WrappedComponent(props, context)];
                }
            }
        };
    };
};

// Loading HOC
const withLoading = (WrappedComponent, loadingConfig = {}) => {
    return (props, context) => {
        const { getState } = context;
        const isLoading = () => getState(loadingConfig.statePath || 'ui.loading', false);
        
        return {
            div: {
                className: 'loading-wrapper',
                children: () => {
                    if (isLoading()) {
                        return [{
                            div: {
                                className: 'loading-overlay',
                                children: [
                                    { Icon: { name: 'spinner' } },
                                    { p: { text: loadingConfig.message || 'Loading...' } }
                                ]
                            }
                        }];
                    }
                    
                    return [{ [WrappedComponent]: props }];
                }
            }
        };
    };
};

// Error boundary HOC
const withErrorBoundary = (WrappedComponent, errorConfig = {}) => {
    return (props, context) => {
        const { getState, setState } = context;
        const error = () => getState(`errors.${errorConfig.key || 'component'}`, null);
        
        const clearError = () => {
            setState(`errors.${errorConfig.key || 'component'}`, null);
        };
        
        return {
            div: {
                className: 'error-boundary',
                children: () => {
                    if (error()) {
                        return [{
                            div: {
                                className: 'error-fallback',
                                children: [
                                    { h3: { text: 'Something went wrong' } },
                                    { p: { text: error() } },
                                    { Button: { 
                                        text: 'Try Again', 
                                        onClick: clearError 
                                    }}
                                ]
                            }
                        }];
                    }
                    
                    try {
                        return [{ [WrappedComponent]: props }];
                    } catch (err) {
                        setState(`errors.${errorConfig.key || 'component'}`, err.message);
                        return [];
                    }
                }
            }
        };
    };
};

// Data fetching HOC
const withData = (WrappedComponent, dataConfig) => {
    return (props, context) => {
        const { getState, setState } = context;
        const cacheKey = dataConfig.cacheKey || dataConfig.url;
        
        const data = () => getState(`cache.${cacheKey}`, null);
        const loading = () => getState(`loading.${cacheKey}`, false);
        const error = () => getState(`errors.${cacheKey}`, null);
        
        const fetchData = async () => {
            setState(`loading.${cacheKey}`, true);
            setState(`errors.${cacheKey}`, null);
            
            try {
                const url = typeof dataConfig.url === 'function' 
                    ? dataConfig.url(props) 
                    : dataConfig.url;
                    
                const response = await fetch(url);
                const result = await response.json();
                
                setState(`cache.${cacheKey}`, result);
            } catch (err) {
                setState(`errors.${cacheKey}`, err.message);
            } finally {
                setState(`loading.${cacheKey}`, false);
            }
        };
        
        return {
            div: {
                onMount: () => {
                    if (!data() && !loading()) {
                        fetchData();
                    }
                },
                children: () => [{
                    [WrappedComponent]: {
                        ...props,
                        data: data(),
                        loading: loading(),
                        error: error(),
                        refetch: fetchData
                    }
                }]
            }
        };
    };
};

// Usage examples
const ProtectedUserProfile = withAuth(withLoading(withData(UserProfile, {
    url: (props) => `/api/users/${props.userId}`,
    cacheKey: 'currentUser'
})));

const SafeDashboard = withErrorBoundary(withAuth(Dashboard), {
    key: 'dashboard'
});
⚠️ Edge Cases to Handle
  • HOC composition order: Order matters for data flow and functionality
  • Props collision: Ensure HOCs don't override important props
  • Performance impact: Consider HOC wrapping depth and re-render frequency

🎨 Render Props Pattern

Flexible component patterns that provide data or functionality through render functions.

Render Props Implementation

// Data provider with render prop
const DataProvider = (props, context) => {
    const { getState, setState } = context;
    const data = () => getState(props.dataPath, null);
    const loading = () => getState(`${props.dataPath}.loading`, false);
    const error = () => getState(`${props.dataPath}.error`, null);
    
    const actions = {
        refetch: async () => {
            setState(`${props.dataPath}.loading`, true);
            try {
                const response = await fetch(props.url);
                const result = await response.json();
                setState(props.dataPath, result);
                setState(`${props.dataPath}.error`, null);
            } catch (err) {
                setState(`${props.dataPath}.error`, err.message);
            } finally {
                setState(`${props.dataPath}.loading`, false);
            }
        },
        
        update: (newData) => {
            setState(props.dataPath, newData);
        },
        
        reset: () => {
            setState(props.dataPath, null);
            setState(`${props.dataPath}.error`, null);
        }
    };
    
    return {
        div: {
            children: () => props.render({
                data: data(),
                loading: loading(),
                error: error(),
                actions
            })
        }
    };
};

// Mouse tracker render prop
const MouseTracker = (props) => {
    let mousePosition = { x: 0, y: 0 };
    
    const updatePosition = (e) => {
        mousePosition = { x: e.clientX, y: e.clientY };
        // Trigger re-render somehow - in real implementation
        // this would use setState or similar
    };
    
    return {
        div: {
            onMouseMove: updatePosition,
            style: {
                height: '100%',
                width: '100%'
            },
            children: () => props.render(mousePosition)
        }
    };
};

// Form state management render prop
const FormProvider = (props, { getState, setState }) => {
    const formPath = props.formPath || 'form';
    const formData = () => getState(formPath, props.initialValues || {});
    const errors = () => getState(`${formPath}.errors`, {});
    const touched = () => getState(`${formPath}.touched`, {});
    
    const formActions = {
        setValue: (field, value) => {
            setState(`${formPath}.${field}`, value);
        },
        
        setError: (field, error) => {
            const currentErrors = errors();
            setState(`${formPath}.errors`, { ...currentErrors, [field]: error });
        },
        
        setTouched: (field, isTouched = true) => {
            const currentTouched = touched();
            setState(`${formPath}.touched`, { ...currentTouched, [field]: isTouched });
        },
        
        reset: () => {
            setState(formPath, props.initialValues || {});
            setState(`${formPath}.errors`, {});
            setState(`${formPath}.touched`, {});
        },
        
        validate: () => {
            if (!props.validationSchema) return true;
            
            const data = formData();
            const newErrors = {};
            
            Object.keys(props.validationSchema).forEach(field => {
                const validator = props.validationSchema[field];
                const error = validator(data[field], data);
                if (error) newErrors[field] = error;
            });
            
            setState(`${formPath}.errors`, newErrors);
            return Object.keys(newErrors).length === 0;
        },
        
        submit: async (onSubmit) => {
            if (formActions.validate()) {
                try {
                    await onSubmit(formData());
                } catch (error) {
                    setState(`${formPath}.submitError`, error.message);
                }
            }
        }
    };
    
    return {
        div: {
            children: () => props.render({
                values: formData(),
                errors: errors(),
                touched: touched(),
                actions: formActions
            })
        }
    };
};

// Usage examples
const UserDataDisplay = () => ({
    DataProvider: {
        url: '/api/user',
        dataPath: 'userData',
        render: ({ data, loading, error, actions }) => {
            if (loading) return [{ div: { text: 'Loading user...' } }];
            if (error) return [{ div: { text: `Error: ${error}` } }];
            if (!data) return [{ div: { text: 'No user data' } }];
            
            return [
                { h2: { text: data.name } },
                { p: { text: data.email } },
                { Button: { 
                    text: 'Refresh', 
                    onClick: actions.refetch 
                }}
            ];
        }
    }
});

const ContactForm = () => ({
    FormProvider: {
        formPath: 'contactForm',
        initialValues: { name: '', email: '', message: '' },
        validationSchema: {
            name: (value) => !value ? 'Name is required' : null,
            email: (value) => !value || !value.includes('@') ? 'Valid email required' : null,
            message: (value) => !value || value.length < 10 ? 'Message must be at least 10 characters' : null
        },
        render: ({ values, errors, touched, actions }) => [
            {
                TextInput: {
                    value: values.name || '',
                    error: touched.name && errors.name,
                    onChange: (value) => actions.setValue('name', value),
                    onBlur: () => actions.setTouched('name')
                }
            },
            {
                TextInput: {
                    type: 'email',
                    value: values.email || '',
                    error: touched.email && errors.email,
                    onChange: (value) => actions.setValue('email', value),
                    onBlur: () => actions.setTouched('email')
                }
            },
            {
                Button: {
                    text: 'Submit',
                    onClick: () => actions.submit(async (data) => {
                        await fetch('/api/contact', {
                            method: 'POST',
                            body: JSON.stringify(data)
                        });
                    })
                }
            }
        ]
    }
});
⚠️ Edge Cases to Handle
  • Render function stability: Ensure render functions don't cause infinite loops
  • Performance considerations: Optimize render prop components for frequent updates
  • Type safety: Document expected render prop parameters clearly

🎯 Pattern Selection Guide

Choose the right component pattern based on your specific use case and requirements.

📊 Simple Display

Use for static content, labels, badges, and basic information display

Low Complexity
Best For
  • Headers and text content
  • Status indicators
  • Image and media display
  • Icons and visual elements

🎛️ Interactive Basic

Perfect for buttons, inputs, and simple user interactions

Low Complexity
Best For
  • Form controls
  • Action buttons
  • Toggle switches
  • Basic user input

📋 List & Data

Ideal for displaying collections, tables, and data-driven content

Medium Complexity
Best For
  • Data tables
  • Product catalogs
  • Search results
  • Infinite scroll lists

📝 Forms

Complex form handling with validation, submission, and error management

High Complexity
Best For
  • User registration
  • Data entry forms
  • Multi-step wizards
  • Settings panels

🔄 Async Data

Components that handle API calls, loading states, and real-time data

High Complexity
Best For
  • API integration
  • Real-time dashboards
  • Live notifications
  • Data synchronization

🏗️ Composition

Advanced patterns for reusable logic and component composition

High Complexity
Best For
  • Cross-cutting concerns
  • Reusable behaviors
  • Complex layouts
  • Plugin architectures

Decision Matrix

Requirement Recommended Pattern Alternative Options
Display user information Simple Display Conditional Rendering
Handle form submission Form Handling Interactive Basic + Validation
Show list of products List Rendering Async Data + List
Reusable authentication Higher-Order Components Render Props
Real-time notifications Async Data WebSocket + Conditional
Complex modal dialog Portal + Composition Conditional + Interactive

✨ Best Practices

Guidelines for writing maintainable, performant, and reusable Juris components.

Component Design Principles

  • Single Responsibility: Each component should have one clear purpose
  • Pure Functions: Same props should always produce the same output
  • Minimal Props: Keep component interfaces simple and focused
  • Composition over Inheritance: Build complex UIs by composing simple components

Performance Guidelines

  • Optimize Reactive Functions: Avoid expensive calculations in reactive functions
  • Use Keys for Lists: Provide stable keys for list items to optimize updates
  • Minimize State Subscriptions: Only subscribe to the state you actually need
  • Lazy Load Components: Load heavy components only when needed

Code Organization

  • Group Related Components: Organize components by feature or domain
  • Extract Common Logic: Use HOCs or render props for shared functionality
  • Document Props: Clearly document expected props and their types
  • Handle Edge Cases: Always consider null, undefined, and error states

Testing Strategies

  • Test Component Logic: Focus on testing component behavior, not implementation
  • Mock External Dependencies: Isolate components from external services
  • Test User Interactions: Verify that user actions produce expected results
  • Test Error Conditions: Ensure components handle errors gracefully

⚠️ Anti-Patterns to Avoid

Common mistakes and patterns that should be avoided in Juris component development.

State Anti-Patterns

// ❌ DON'T: Mutate state directly
const BadComponent = (props, { getState, setState }) => {
    const data = getState('data', []);
    data.push(newItem); // Direct mutation!
    setState('data', data);
};

// ✅ DO: Create new state objects
const GoodComponent = (props, { getState, setState }) => {
    const data = getState('data', []);
    setState('data', [...data, newItem]); // Immutable update
};

Performance Anti-Patterns

// ❌ DON'T: Expensive operations in reactive functions
const BadComponent = (props, { getState }) => ({
    div: {
        text: () => {
            const items = getState('items', []);
            // Expensive calculation on every render!
            return items.reduce((sum, item) => sum + complexCalculation(item), 0);
        }
    }
});

// ✅ DO: Use computed state or memoization
const GoodComponent = (props, { getState }) => ({
    div: {
        text: () => getState('computedTotal', 0) // Pre-calculated value
    }
});

Common Mistakes

  • Over-engineering: Don't use complex patterns for simple use cases
  • Props drilling: Avoid passing props through many component layers
  • Mixed concerns: Don't mix UI logic with business logic in components
  • Missing error handling: Always handle potential error states
  • Ignoring accessibility: Include proper ARIA labels and keyboard navigation