# React Abit Cache 🚀

A powerful React caching library with features like optimistic updates, cache invalidation, and more.

## Features

- 🕒 Data caching
- ♻️ Automatic revalidation on focus, reconnect, or interval
- ⚡ Optimistic updates for mutations
- 🧩 Suspense mode support
- 🔍 Deep comparison for efficient updates
- 🗑️ Cache invalidation by key or pattern matching
- 🔄 Retry mechanism for failed requests
- 📦 Lightweight and tree-shakable

## Installation

```bash
npm install abit-cache
# or
yarn add abit-cache
```

### Quick Start

1. Wrap your app with CacheProvider

```tsx
import { CacheProvider } from 'abit-cache';

function App() {
  return (
    <CacheProvider
      value={{
        staleTime: 1000 * 60 * 5, // 5 minutes
        isRevalidateOnFocus: true,
      }}
    >
      <YourApp />
    </CacheProvider>
  );
}
```

2. Use useQuery for data fetching

```tsx
import { useQuery } from 'abit-cache';

function UserProfile({ userId }) {
  const { data, error, isLoading } = useQuery(
    ['user', userId],
    async ([, id]) => {
      const response = await fetch(`/api/users/${id}`);
      return response.json();
    },
    {
      staleTime: 1000 * 30, // 30 seconds
      onSuccess: (data) => console.log('User loaded:', data),
    },
  );
  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
}

return <div>{data.name}</div>;
```

3. Use useMutation for data updates

```tsx
import { useMutation } from 'abit-cache';

function UpdateProfile() {
  const { mutate, isMutating } = useMutation(
    'update-profile',
    async (variables) => {
      const response = await fetch('/api/profile', {
        method: 'POST',
        body: JSON.stringify(variables),
      });
      return response.json();
    },
    {
      optimisticData: (currentData) => ({
        ...currentData,
        ...variables,
      }),
    },
  );

  const handleSubmit = (data) => {
    mutate(data, {
      onSuccess: () => alert('Profile updated!'),
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <button type="submit" disabled={isMutating}>
        {isMutating ? 'Saving...' : 'Save'}
      </button>
    </form>
  );
}
```

### API Reference

#### CacheProvider

| Prop  | Type         | Description                         |
| ----- | ------------ | ----------------------------------- |
| value | TCacheConfig | Configuration options for the cache |

### useQuery

```ts
const {
  data,
  error,
  isLoading,
  revalidate,
  invalidate,
  invalidateMatching,
  clear,
} = useQuery<TData>(key, fetcher, options);
```

### Parameters:

| Parameter | Type                            | Description                                   |
| --------- | ------------------------------- | --------------------------------------------- |
| `key`     | `string` \| `array` \| `object` | Unique key for the query                      |
| `fetcher` | `function`                      | Function that returns a Promise with the data |
| `options` | `object` (optional)             | Configuration object                          |

### Options:

| Option                    | Type       | Default        | Description                          |
| ------------------------- | ---------- | -------------- | ------------------------------------ |
| `isEnabled`               | `boolean`  | `true`         | Whether the query should execute     |
| `isSuspense`              | `boolean`  | `false`        | Enable Suspense mode                 |
| `staleTime`               | `number`   | `0`            | Time until data is considered stale  |
| `isRevalidateIfStale`     | `boolean`  | `true`         | Auto-revalidate when stale           |
| `isRevalidateOnFocus`     | `boolean`  | `false`        | Revalidate when window regains focus |
| `isRevalidateOnReconnect` | `boolean`  | `false`        | Revalidate when network reconnects   |
| `isKeepPreviousData`      | `boolean`  | `false`        | Keep previous data while loading     |
| `shouldRetryOnError`      | `boolean`  | `true`         | Retry on error                       |
| `errorRetryInterval`      | `number`   | `5000`         | Delay between retries (ms)           |
| `errorRetryCount`         | `number`   | `3`            | Max retry attempts                   |
| `dedupingInterval`        | `number`   | `2000`         | Dedupe simultaneous requests (ms)    |
| `focusThrottleInterval`   | `number`   | `5000`         | Throttle focus revalidation (ms)     |
| `onSuccess`               | `function` | `-`            | Callback on successful fetch         |
| `onError`                 | `function` | `-`            | Callback on error                    |
| `onErrorRetry`            | `function` | `-`            | Custom error retry handler           |
| `isDeepEqual`             | `function` | `shallowEqual` | Custom equality function             |

### useMutation

```ts
const {
  isMutating,
  error,
  data,
  mutate,
  invalidate,
  invalidateMatching,
  clear,
} = useMutation<TData, TVariables>(key, fetcher, options);
```

### Parameters

| Parameter | Type                | Description                                           |
| --------- | ------------------- | ----------------------------------------------------- |
| `key`     | `string`            | Unique key for the mutation                           |
| `fetcher` | `function`          | Function that returns a Promise with the updated data |
| `options` | `object` (optional) | Configuration object (optional)                       |

### Options

| Option              | Type                 | Default | Description                         |
| ------------------- | -------------------- | ------- | ----------------------------------- |
| `isRevalidate`      | `boolean`            | `true`  | Revalidate after mutation           |
| `isRollbackOnError` | `boolean`            | `true`  | Rollback optimistic update on error |
| `optimisticData`    | `Data` \| `function` | `-`     | Data to show immediately            |
| `onSuccess`         | `function`           | `-`     | Callback on successful mutation     |
| `onError`           | `function`           | `-`     | Callback on error                   |
| `onSettled`         | `function`           | `-`     | Callback after mutation completes   |

```ts
const { invalidateMatching } = useQuery(...);

// Delete all cache entries starting with 'user'
invalidateMatching('^user');
```

### Suspense Mode

```tsx
function UserProfile({ userId }) {
  const { data } = useQuery(['user', userId], fetchUser, { isSuspense: true });

  return <div>{data.name}</div>;
}

function App() {
  return (
    <Suspense fallback={<div>Loading user...</div>}>
      <UserProfile userId="123" />
    </Suspense>
  );
}
```

### Global Configuration

```tsx
<CacheProvider
  value={{
    staleTime: 1000 * 60 * 10, // 10 minutes
    isRevalidateOnFocus: true,
    isRevalidateOnReconnect: true,
    errorRetryCount: 5,
    onError: (err) => trackError(err),
  }}
>
  <App />
</CacheProvider>
```

> TypeScript Support
> The library is fully typed with TypeScript. All hooks and types are exported:

```ts
import {
  useQuery,
  useMutation,
  CacheProvider,
  type TCacheOptions,
  type TMutateOptions,
} from 'abit-cache';
```

Contributing
Contributions are welcome! Please open an issue or PR on GitHub.

### License

MIT
