# ReactJS and React Native Compatibility

SilentCronX is compatible with ReactJS and React Native through backend APIs.

The scheduler uses Node.js timers, worker threads, storage adapters, locks, and process shutdown hooks. Those APIs belong on the backend, not inside browser or mobile JavaScript runtimes. ReactJS and React Native apps should call your backend API to trigger jobs, read job status, show queue stats, and display health.

## Recommended Architecture

```text
ReactJS / React Native UI
  -> native fetch / createSilentCronXClient
  -> Node.js backend route
  -> SilentCronX scheduler
  -> queue, worker, cron, retry, status, health
```

## Backend Route Example

```ts
import { createSilentCronX } from "silent-cronx";

const scheduler = createSilentCronX({
  timezone: "Asia/Kolkata",
  maxWorkers: 4,
  maxConcurrency: 25,
  defaultTimeout: 30_000,
  eventHistoryLimit: 250,
});

scheduler.queue("report-queue", {
  concurrency: 5,
  retry: { attempts: 3, backoff: "exponential", delayMs: 1000, jitter: true },
  processor: async ({ payload, progress }) => {
    await progress({ percent: 20, message: "Started from frontend request" });
    await progress({ percent: 70, message: "Processing report" });
    await progress({ percent: 100, message: "Report complete" });
    return { ok: true, payload };
  },
});

scheduler.start();

export async function createReportJob(body: { reportType: string }) {
  const jobId = await scheduler.addJob("report-queue", {
    name: "frontend-report",
    payload: body,
    priority: 10,
  });

  return { accepted: true, jobId };
}

export async function getSchedulerDashboard() {
  return {
    health: await scheduler.getHealth(),
    queueStats: scheduler.getQueueStats(),
    recentEvents: scheduler.getEventHistory(),
  };
}
```

## Native Client

```ts
import { createSilentCronXClient } from "silent-cronx";

const client = createSilentCronXClient({
  baseUrl: "http://localhost:4517",
  headers: {
    Authorization: "Bearer YOUR_API_TOKEN",
  },
});

const { jobId } = await client.triggerJob("report", {
  payload: { reportType: "daily" },
  priority: 10,
});

const job = await client.getJob(jobId);
const progress = job?.progress;
```

## ReactJS Hook

```tsx
import { useCallback, useEffect, useMemo, useState } from "react";
import { createSilentCronXClient, type JobRecord } from "silent-cronx";

type JobResponse = {
  accepted: boolean;
  jobId: string;
};

export function useSilentCronXJob(baseUrl = "http://localhost:4517") {
  const client = useMemo(() => createSilentCronXClient({ baseUrl }), [baseUrl]);
  const [loading, setLoading] = useState(false);
  const [jobId, setJobId] = useState<string | null>(null);
  const [job, setJob] = useState<JobRecord | null>(null);
  const [error, setError] = useState<string | null>(null);

  const triggerReport = useCallback(
    async (reportType: string) => {
      setLoading(true);
      setError(null);
      try {
        const data = await client.triggerJob("report", {
          payload: { reportType },
          priority: 10,
        });
        setJobId(data.jobId);
        return data;
      } catch (caught) {
        const message = caught instanceof Error ? caught.message : "Unknown request error";
        setError(message);
        throw caught;
      } finally {
        setLoading(false);
      }
    },
    [client]
  );

  useEffect(() => {
    if (!jobId) return;
    const timer = setInterval(() => {
      void client.getJob(jobId).then(setJob);
    }, 1000);
    return () => clearInterval(timer);
  }, [client, jobId]);

  return { loading, error, jobId, job, progress: job?.progress, triggerReport };
}
```

## React Native Screen

```tsx
import { useState } from "react";
import { ActivityIndicator, Button, Text, View } from "react-native";
import { createSilentCronXClient } from "silent-cronx";

const API_BASE_URL = "http://localhost:4517";

export function SilentCronXJobScreen() {
  const client = createSilentCronXClient({ baseUrl: API_BASE_URL });
  const [loading, setLoading] = useState(false);
  const [jobId, setJobId] = useState<string | null>(null);
  const [offlineQueue, setOfflineQueue] = useState<Array<{ reportType: string }>>([]);

  async function triggerReport() {
    setLoading(true);
    try {
      const data = await client.triggerJob("report", {
        payload: { reportType: "mobile-daily" },
      });
      setJobId(data.jobId);
    } catch {
      setOfflineQueue((items) => [...items, { reportType: "mobile-daily" }]);
    } finally {
      setLoading(false);
    }
  }

  return (
    <View>
      <Button title="Start SilentCronX Job" onPress={triggerReport} disabled={loading} />
      {loading ? <ActivityIndicator /> : null}
      {jobId ? <Text>Job queued: {jobId}</Text> : null}
      {offlineQueue.length > 0 ? <Text>Offline queue: {offlineQueue.length}</Text> : null}
    </View>
  );
}
```

## Realtime Events with Native SSE

The live demo exposes `GET /events/stream` using standard Server-Sent Events. ReactJS can use the browser-native `EventSource`.

```ts
const events = new EventSource("http://localhost:4517/events/stream");

events.addEventListener("job:progress", (message) => {
  const event = JSON.parse(message.data);
  console.log(event.event.progress);
});
```

React Native does not include `EventSource` in every runtime, so the no-dependency default is polling with `client.getJob(jobId)`. If your app already has a native event-stream bridge, you can point it at the same endpoint.

## Compatibility Notes

- ReactJS 18 and newer can connect through native `fetch`, `createSilentCronXClient`, or browser `EventSource`.
- React Native 0.7x and newer can connect through built-in `fetch` or `createSilentCronXClient`.
- SilentCronX should not be bundled into browser or mobile frontend code.
- Use environment-specific API base URLs for local, staging, and production builds.
- Protect admin routes that expose queue stats, event history, cancel, pause, resume, or clear operations.
- Use HTTPS and auth tokens for production mobile apps.
