Skip to content

React Bindings โ€‹

The official React bindings for qiankun provide a declarative way to integrate micro applications into your React main application. The @qiankunjs/react package offers a powerful <MicroApp /> component with built-in loading states, error handling, and TypeScript support.

๐Ÿ“ฆ Installation โ€‹

bash
npm install @qiankunjs/react

Requirements:

  • React โ‰ฅ 16.9.0
  • qiankun โ‰ฅ 3.0.0

๐Ÿš€ Quick Start โ€‹

Basic Usage โ€‹

tsx
import React from 'react';
import { MicroApp } from '@qiankunjs/react';

function App() {
  return (
    <div className="main-app">
      <h1>Main Application</h1>
      <MicroApp 
        name="dashboard" 
        entry="//localhost:8080" 
      />
    </div>
  );
}

export default App;

With Loading State โ€‹

tsx
import React from 'react';
import { MicroApp } from '@qiankunjs/react';

function App() {
  return (
    <MicroApp 
      name="dashboard" 
      entry="//localhost:8080" 
      autoSetLoading // Enable automatic loading state
    />
  );
}

With Error Handling โ€‹

tsx
import React from 'react';
import { MicroApp } from '@qiankunjs/react';

function App() {
  return (
    <MicroApp 
      name="dashboard" 
      entry="//localhost:8080" 
      autoSetLoading
      autoCaptureError // Enable automatic error handling
    />
  );
}

๐ŸŽฏ Component API โ€‹

Props โ€‹

PropTypeRequiredDefaultDescription
namestringโœ…-Unique name for the micro application
entrystringโœ…-Entry URL of the micro application
autoSetLoadingbooleanโŒfalseAutomatically manage loading state
autoCaptureErrorbooleanโŒfalseAutomatically handle errors
loader(loading: boolean) => React.ReactNodeโŒundefinedCustom loading component
errorBoundary(error: any) => React.ReactNodeโŒundefinedCustom error component
classNamestringโŒundefinedCSS class for the micro app container
wrapperClassNamestringโŒundefinedCSS class for the wrapper (when using loader/errorBoundary)
settingsAppConfigurationโŒ{}qiankun configuration options
lifeCyclesLifeCyclesโŒundefinedLifecycle hooks

Additional Props โ€‹

Any additional props passed to <MicroApp /> will be forwarded to the micro application as props:

tsx
<MicroApp
  name="user-profile"
  entry="//localhost:8080"
  // These props are passed to the micro app
  userId={user.id}
  theme="dark"
  permissions={user.permissions}
/>

๐Ÿ”„ Lifecycle Management โ€‹

Using Ref to Access Micro App Instance โ€‹

tsx
import React, { useRef, useEffect } from 'react';
import { MicroApp } from '@qiankunjs/react';

function App() {
  const microAppRef = useRef<any>();

  useEffect(() => {
    // Get micro app status
    console.log(microAppRef.current?.getStatus());
  }, []);

  const handleUnmount = () => {
    microAppRef.current?.unmount();
  };

  return (
    <div>
      <button onClick={handleUnmount}>Unmount Micro App</button>
      <MicroApp 
        ref={microAppRef}
        name="dashboard" 
        entry="//localhost:8080" 
      />
    </div>
  );
}

App Status โ€‹

The micro app instance provides these status values:

  • NOT_LOADED - Initial state, not loaded yet
  • LOADING_SOURCE_CODE - Loading application resources
  • NOT_BOOTSTRAPPED - Resources loaded, not bootstrapped
  • BOOTSTRAPPING - Running bootstrap lifecycle
  • NOT_MOUNTED - Bootstrapped but not mounted
  • MOUNTING - Running mount lifecycle
  • MOUNTED - Successfully mounted and running
  • UPDATING - Running update lifecycle
  • UNMOUNTING - Running unmount lifecycle
  • UNLOADING - Cleaning up resources

๐ŸŽจ Customization โ€‹

Custom Loading Component โ€‹

tsx
import React from 'react';
import { MicroApp } from '@qiankunjs/react';
import { Spin, Alert } from 'antd';

const CustomLoader: React.FC<{ loading: boolean }> = ({ loading }) => {
  if (!loading) return null;
  
  return (
    <div style={{ textAlign: 'center', padding: '50px' }}>
      <Spin size="large" />
      <p style={{ marginTop: '16px' }}>Loading micro application...</p>
    </div>
  );
};

function App() {
  return (
    <MicroApp 
      name="dashboard" 
      entry="//localhost:8080" 
      loader={(loading) => <CustomLoader loading={loading} />}
    />
  );
}

Custom Error Boundary โ€‹

tsx
import React from 'react';
import { MicroApp } from '@qiankunjs/react';
import { Alert, Button } from 'antd';

const CustomErrorBoundary: React.FC<{ error: Error }> = ({ error }) => {
  const handleRetry = () => {
    window.location.reload();
  };

  return (
    <div style={{ padding: '20px' }}>
      <Alert
        message="Micro Application Error"
        description={error.message}
        type="error"
        action={
          <Button size="small" danger onClick={handleRetry}>
            Retry
          </Button>
        }
      />
    </div>
  );
};

function App() {
  return (
    <MicroApp 
      name="dashboard" 
      entry="//localhost:8080" 
      errorBoundary={(error) => <CustomErrorBoundary error={error} />}
    />
  );
}

Styling โ€‹

tsx
import React from 'react';
import { MicroApp } from '@qiankunjs/react';
import './MicroApp.css';

function App() {
  return (
    <MicroApp 
      name="dashboard" 
      entry="//localhost:8080" 
      className="micro-app-container"
      wrapperClassName="micro-app-wrapper"
      autoSetLoading
    />
  );
}
css
/* MicroApp.css */
.micro-app-wrapper {
  border: 1px solid #e8e8e8;
  border-radius: 6px;
  overflow: hidden;
}

.micro-app-container {
  min-height: 400px;
  background: #fafafa;
}

๐Ÿ”ง Advanced Usage โ€‹

Multiple Micro Apps โ€‹

tsx
import React, { useState } from 'react';
import { MicroApp } from '@qiankunjs/react';
import { Tabs } from 'antd';

const { TabPane } = Tabs;

function Dashboard() {
  const [activeTab, setActiveTab] = useState('dashboard');

  return (
    <div className="multi-app-container">
      <Tabs activeKey={activeTab} onChange={setActiveTab}>
        <TabPane tab="Dashboard" key="dashboard">
          <MicroApp 
            name="dashboard" 
            entry="//localhost:8080" 
            autoSetLoading
          />
        </TabPane>
        <TabPane tab="Analytics" key="analytics">
          <MicroApp 
            name="analytics" 
            entry="//localhost:8081" 
            autoSetLoading
          />
        </TabPane>
        <TabPane tab="Settings" key="settings">
          <MicroApp 
            name="settings" 
            entry="//localhost:8082" 
            autoSetLoading
          />
        </TabPane>
      </Tabs>
    </div>
  );
}

Conditional Loading โ€‹

tsx
import React, { useState } from 'react';
import { MicroApp } from '@qiankunjs/react';

function ConditionalApp() {
  const [showMicroApp, setShowMicroApp] = useState(false);
  const [user, setUser] = useState(null);

  // Only load micro app when user is authenticated
  if (!user) {
    return <div>Please log in to continue</div>;
  }

  return (
    <div>
      <button onClick={() => setShowMicroApp(!showMicroApp)}>
        {showMicroApp ? 'Hide' : 'Show'} Micro App
      </button>
      
      {showMicroApp && (
        <MicroApp 
          name="protected-app" 
          entry="//localhost:8080" 
          userId={user.id}
          permissions={user.permissions}
          autoSetLoading
          autoCaptureError
        />
      )}
    </div>
  );
}

Dynamic Entry URLs โ€‹

tsx
import React, { useState } from 'react';
import { MicroApp } from '@qiankunjs/react';

function DynamicApp() {
  const [environment, setEnvironment] = useState('development');
  
  const entryUrls = {
    development: '//localhost:8080',
    staging: '//staging.example.com',
    production: '//app.example.com'
  };

  return (
    <div>
      <select value={environment} onChange={(e) => setEnvironment(e.target.value)}>
        <option value="development">Development</option>
        <option value="staging">Staging</option>
        <option value="production">Production</option>
      </select>
      
      <MicroApp 
        name="dynamic-app" 
        entry={entryUrls[environment]} 
        environment={environment}
        autoSetLoading
      />
    </div>
  );
}

๐ŸŽฎ State Management โ€‹

Using Context to Share State โ€‹

tsx
import React, { createContext, useContext, useState } from 'react';
import { MicroApp } from '@qiankunjs/react';

// Create a context for shared state
const AppContext = createContext();

function MainApp() {
  const [sharedState, setSharedState] = useState({
    user: { id: 1, name: 'John' },
    theme: 'dark'
  });

  return (
    <AppContext.Provider value={{ sharedState, setSharedState }}>
      <div className="main-app">
        <Navigation />
        <MicroAppContainer />
      </div>
    </AppContext.Provider>
  );
}

function MicroAppContainer() {
  const { sharedState } = useContext(AppContext);
  
  return (
    <MicroApp 
      name="micro-app" 
      entry="//localhost:8080" 
      // Pass context data as props
      user={sharedState.user}
      theme={sharedState.theme}
      autoSetLoading
    />
  );
}

Communication Between Apps โ€‹

tsx
import React, { useEffect, useRef } from 'react';
import { MicroApp } from '@qiankunjs/react';

function CommunicatingApps() {
  const microApp1Ref = useRef();
  const microApp2Ref = useRef();

  useEffect(() => {
    // Set up communication channel
    window.appCommunication = {
      sendMessage: (from, to, message) => {
        const event = new CustomEvent('microAppMessage', {
          detail: { from, to, message }
        });
        window.dispatchEvent(event);
      }
    };

    // Listen for messages
    const handleMessage = (event) => {
      console.log('Message received:', event.detail);
    };

    window.addEventListener('microAppMessage', handleMessage);

    return () => {
      window.removeEventListener('microAppMessage', handleMessage);
      delete window.appCommunication;
    };
  }, []);

  return (
    <div style={{ display: 'flex' }}>
      <div style={{ flex: 1 }}>
        <MicroApp 
          ref={microApp1Ref}
          name="app1" 
          entry="//localhost:8080" 
          autoSetLoading
        />
      </div>
      <div style={{ flex: 1 }}>
        <MicroApp 
          ref={microApp2Ref}
          name="app2" 
          entry="//localhost:8081" 
          autoSetLoading
        />
      </div>
    </div>
  );
}

๐Ÿ”’ TypeScript Support โ€‹

Typed Props โ€‹

tsx
import React from 'react';
import { MicroApp } from '@qiankunjs/react';

interface UserProfileProps {
  userId: string;
  theme: 'light' | 'dark';
  permissions: string[];
}

// Type the additional props
const UserProfileApp: React.FC = () => {
  const user = getCurrentUser();
  
  return (
    <MicroApp 
      name="user-profile" 
      entry="//localhost:8080"
      // TypeScript will validate these props
      userId={user.id}
      theme="dark"
      permissions={user.permissions}
      autoSetLoading
    />
  );
};

Custom Hook for Micro App โ€‹

tsx
import { useRef, useEffect, useState } from 'react';
import type { MicroApp as MicroAppType } from 'qiankun';

interface UseMicroAppOptions {
  onStatusChange?: (status: string) => void;
  onError?: (error: Error) => void;
}

export function useMicroApp(options: UseMicroAppOptions = {}) {
  const microAppRef = useRef<MicroAppType>();
  const [status, setStatus] = useState<string>('NOT_LOADED');
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    const checkStatus = () => {
      if (microAppRef.current) {
        const currentStatus = microAppRef.current.getStatus();
        if (currentStatus !== status) {
          setStatus(currentStatus);
          options.onStatusChange?.(currentStatus);
        }
      }
    };

    const interval = setInterval(checkStatus, 1000);
    return () => clearInterval(interval);
  }, [status, options]);

  const handleError = (err: Error) => {
    setError(err);
    options.onError?.(err);
  };

  return {
    microAppRef,
    status,
    error,
    handleError
  };
}

// Usage
function App() {
  const { microAppRef, status, error } = useMicroApp({
    onStatusChange: (status) => console.log('Status changed:', status),
    onError: (error) => console.error('App error:', error)
  });

  return (
    <div>
      <p>Status: {status}</p>
      {error && <p>Error: {error.message}</p>}
      <MicroApp 
        ref={microAppRef}
        name="dashboard" 
        entry="//localhost:8080" 
      />
    </div>
  );
}

๐Ÿš€ Performance Optimization โ€‹

Lazy Loading โ€‹

tsx
import React, { Suspense, lazy } from 'react';

// Lazy load the MicroApp component
const LazyMicroApp = lazy(() => 
  import('@qiankunjs/react').then(module => ({ default: module.MicroApp }))
);

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyMicroApp 
        name="dashboard" 
        entry="//localhost:8080" 
        autoSetLoading
      />
    </Suspense>
  );
}

Memoization โ€‹

tsx
import React, { memo, useMemo } from 'react';
import { MicroApp } from '@qiankunjs/react';

const MemoizedMicroApp = memo(MicroApp);

function OptimizedApp({ user, settings }) {
  const microAppProps = useMemo(() => ({
    userId: user.id,
    theme: settings.theme,
    language: settings.language
  }), [user.id, settings.theme, settings.language]);

  return (
    <MemoizedMicroApp 
      name="optimized-app" 
      entry="//localhost:8080" 
      {...microAppProps}
      autoSetLoading
    />
  );
}

๐Ÿ› Error Handling & Debugging โ€‹

Development Mode Error Handling โ€‹

tsx
import React from 'react';
import { MicroApp } from '@qiankunjs/react';

function DevMicroApp() {
  const isDevelopment = process.env.NODE_ENV === 'development';

  const handleError = (error: Error) => {
    console.error('Micro app error:', error);
    
    if (isDevelopment) {
      // Show detailed error in development
      return (
        <div style={{ padding: '20px', background: '#ffe6e6' }}>
          <h3>Development Error</h3>
          <pre>{error.stack}</pre>
          <button onClick={() => window.location.reload()}>
            Reload App
          </button>
        </div>
      );
    }
    
    // Show user-friendly error in production
    return (
      <div style={{ padding: '20px', textAlign: 'center' }}>
        <p>Something went wrong. Please try again later.</p>
      </div>
    );
  };

  return (
    <MicroApp 
      name="dashboard" 
      entry="//localhost:8080" 
      errorBoundary={handleError}
      autoSetLoading
    />
  );
}

๐Ÿ“š Best Practices โ€‹

1. Use Descriptive Names โ€‹

tsx
// โœ… Good: Descriptive names
<MicroApp name="user-dashboard" entry="//localhost:8080" />
<MicroApp name="order-management" entry="//localhost:8081" />

// โŒ Bad: Generic names
<MicroApp name="app1" entry="//localhost:8080" />
<MicroApp name="app2" entry="//localhost:8081" />

2. Always Handle Loading States โ€‹

tsx
// โœ… Good: Handle loading states
<MicroApp 
  name="dashboard" 
  entry="//localhost:8080" 
  autoSetLoading
  loader={(loading) => <CustomSpinner loading={loading} />}
/>

// โŒ Bad: No loading indication
<MicroApp name="dashboard" entry="//localhost:8080" />

3. Implement Error Boundaries โ€‹

tsx
// โœ… Good: Handle errors gracefully
<MicroApp 
  name="dashboard" 
  entry="//localhost:8080" 
  autoCaptureError
  errorBoundary={(error) => <ErrorFallback error={error} />}
/>

4. Use Environment-specific Configurations โ€‹

tsx
// โœ… Good: Environment-aware
const config = {
  development: { entry: '//localhost:8080', debug: true },
  production: { entry: '//app.example.com', debug: false }
};

<MicroApp 
  name="dashboard" 
  entry={config[process.env.NODE_ENV].entry}
  debug={config[process.env.NODE_ENV].debug}
/>

Released under the MIT License.