npm package: @mxweb/react-polyfill
Description: React polyfills for modern hooks in older React versions (16.8+)
Author: MXWeb Team
Latest version: 0.0.1

# @mxweb/react-polyfill

React polyfills for modern hooks in older React versions (16.8+)

[![npm version](https://img.shields.io/npm/v/@mxweb/react-polyfill.svg)](https://www.npmjs.com/package/@mxweb/react-polyfill)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Features

- 🔄 **useSyncExternalStore** - Polyfill for React 18's useSyncExternalStore hook
- 🎨 **useIsomorphicLayoutEffect** - SSR-safe useLayoutEffect alternative
- 🚀 **React Module Augmentation** - Use polyfills directly from 'react' module
- 📦 **Tree-shakeable** - Only bundle what you use
- 🔒 **Type-safe** - Full TypeScript support
- 🪶 **Lightweight** - Minimal bundle size impact

## Installation

```bash
npm install @mxweb/react-polyfill
# or
yarn add @mxweb/react-polyfill
# or
pnpm add @mxweb/react-polyfill
```

## Usage

### Option 1: Direct Import (Recommended)

Import hooks directly from the package:

```typescript
import { useIsomorphicLayoutEffect, useSyncExternalStore } from '@mxweb/react-polyfill';

function MyComponent() {
  // Use isomorphic layout effect (safe for SSR)
  useIsomorphicLayoutEffect(() => {
    const height = element.offsetHeight;
    setHeight(height);
  }, [element]);

  // Use sync external store
  const width = useSyncExternalStore(
    (callback) => {
      window.addEventListener('resize', callback);
      return () => window.removeEventListener('resize', callback);
    },
    () => window.innerWidth,
    () => 0 // Server-side default
  );

  return <div>Width: {width}</div>;
}
```

### Option 2: React Module Augmentation (Recommended for Global Setup)

Import the augmentation module **once** in your app's entry point, then use hooks from 'react' everywhere:

#### Setup in Entry Point

Choose the appropriate setup based on your framework:

**React (Vite/CRA):**
```typescript
// src/main.tsx or src/index.tsx
import '@mxweb/react-polyfill/react';
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
```

**Next.js:**
```typescript
// pages/_app.tsx or app/layout.tsx
import '@mxweb/react-polyfill/react';
import type { AppProps } from 'next/app';

export default function App({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />;
}
```

**Remix:**
```typescript
// app/root.tsx
import '@mxweb/react-polyfill/react';
// ... rest of your root component
```

**Or create a dedicated setup file:**
```typescript
// src/setup.ts
import '@mxweb/react-polyfill/react';

// Then in your entry file:
// src/index.tsx
import './setup';
import React from 'react';
// ... rest of your code
```

#### Usage After Setup

After the global setup, you can import and use the polyfills from 'react' in any component:

```typescript
// In any component file - no need to import the polyfill package
import { useIsomorphicLayoutEffect, useSyncExternalStore } from 'react';

function MyComponent() {
  useIsomorphicLayoutEffect(() => {
    // Your layout effect code
  }, []);

  const value = useSyncExternalStore(subscribe, getSnapshot);
  
  return <div>{value}</div>;
}
```

**Benefits of Module Augmentation:**
- ✅ No need to change imports in existing code
- ✅ Works seamlessly with libraries expecting React 18+ API
- ✅ Automatic fallback to native implementation in React 18+
- ✅ Full TypeScript support

## API

### `useIsomorphicLayoutEffect`

A hook that uses `useLayoutEffect` on the client and `useEffect` on the server.

```typescript
useIsomorphicLayoutEffect(effect: EffectCallback, deps?: DependencyList): void
```

**Use cases:**
- DOM measurements
- Scroll position management
- Animation setup
- Any synchronous DOM manipulation that needs to happen before paint

**Example:**
```typescript
import { useIsomorphicLayoutEffect } from '@mxweb/react-polyfill';

function Component() {
  const ref = useRef<HTMLDivElement>(null);
  
  useIsomorphicLayoutEffect(() => {
    if (ref.current) {
      const height = ref.current.offsetHeight;
      console.log('Height:', height);
    }
  }, []);
  
  return <div ref={ref}>Content</div>;
}
```

### `useSyncExternalStore`

A polyfill for React 18's `useSyncExternalStore` hook. Subscribes to an external store.

```typescript
useSyncExternalStore<Snapshot>(
  subscribe: (onStoreChange: () => void) => () => void,
  getSnapshot: () => Snapshot,
  getServerSnapshot?: () => Snapshot
): Snapshot
```

**Parameters:**
- `subscribe` - Function that subscribes to the store and returns an unsubscribe function
- `getSnapshot` - Function that returns the current store value
- `getServerSnapshot` - Optional function that returns the server-side value

**Example:**
```typescript
import { useSyncExternalStore } from '@mxweb/react-polyfill';

// Window width store
function useWindowWidth() {
  return useSyncExternalStore(
    (callback) => {
      window.addEventListener('resize', callback);
      return () => window.removeEventListener('resize', callback);
    },
    () => window.innerWidth,
    () => 0 // Default for SSR
  );
}

// Custom store
const store = {
  listeners: new Set(),
  value: 0,
  subscribe(callback) {
    this.listeners.add(callback);
    return () => this.listeners.delete(callback);
  },
  getSnapshot() {
    return this.value;
  },
  setValue(newValue) {
    this.value = newValue;
    this.listeners.forEach(listener => listener());
  }
};

function Counter() {
  const value = useSyncExternalStore(
    (callback) => store.subscribe(callback),
    () => store.getSnapshot(),
    () => 0
  );
  
  return (
    <div>
      <p>Value: {value}</p>
      <button onClick={() => store.setValue(value + 1)}>
        Increment
      </button>
    </div>
  );
}
```

## TypeScript

Full TypeScript support is included. When using React module augmentation, the types are automatically available:

```typescript
// After importing '@mxweb/react-polyfill/react'
import { useIsomorphicLayoutEffect, useSyncExternalStore } from 'react';
// TypeScript knows about these hooks ✅
```

## React Version Compatibility

| React Version | useSyncExternalStore | useIsomorphicLayoutEffect |
|---------------|---------------------|---------------------------|
| 16.8 - 17.x   | ✅ Polyfill         | ✅ Available              |
| 18.0+         | ✅ Native           | ✅ Available              |

## Bundle Size

- Core polyfills: ~1KB gzipped
- Tree-shakeable: Only bundle what you use
- Zero dependencies (except React peer dependency)

## License

MIT © [MXWeb Team](https://github.com/mxwebio)

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## Support

- 📝 [Documentation](https://github.com/mxwebio/mxweb-react-polyfill#readme)
- 🐛 [Issue Tracker](https://github.com/mxwebio/mxweb-react-polyfill/issues)
- 💬 [Discussions](https://github.com/mxwebio/mxweb-react-polyfill/discussions)