# React Compiler

Migrate `useCallStateHooks()` usage to be compatible with React Compiler's static analysis requirements.

## Best Practices

- Call `useCallStateHooks()` at module scope, not inside components.
- Alias the import: `import { useCallStateHooks as getCallStateHooks }`.
- Destructure hooks at module level, use them normally in components.
- Use `@stream-io/video-codemod` for automated migration.

## Problem

When using React Compiler, calling `useCallStateHooks()` inside a component violates React Compiler's static analysis requirements. These requirements are enforced during development by ESLint, which reports the error shown below.

```text
ESLint: Hooks must be the same function on every render, but this value may change over time to a different function.
See https://react.dev/reference/rules/react-calls-components-and-hooks#dont-dynamically-use-hooks (react-compiler/react-compiler)
```

### Why this breaks with React Compiler

`useCallStateHooks()` is not a React hook. It’s a factory function that returns hooks.

Calling it inside a component creates hook function references during render. Even if those references are stable, React Compiler cannot verify that at compile time. Because hook functions must be statically known and identical on every render, this pattern is rejected.

## Solution

Call `useCallStateHooks()` once at module scope, then destructure the hooks you need. Use those hooks normally inside your components.

### Migration pattern

1. Alias the import to avoid naming confusion (import as `getCallStateHooks`)
2. Call the factory once at module scope
3. Destructure hooks at the top level
4. Use the hooks normally inside components

### What not to do

Do not call the factory inside the component:

```tsx
import { useCallStateHooks as getCallStateHooks } from "@stream-io/video-react-sdk";

const MyComponent = () => {
  const hooks = getCallStateHooks(); // ❌ still dynamic, still triggers error
  const { useCallCallingState } = hooks;
  // ...
};
```

### Before / After

**Before:**

```tsx {4}
import { useCallStateHooks } from "@stream-io/video-react-sdk";

const MyComponent = () => {
  const { useCallCallingState, useParticipants } = useCallStateHooks();
  const callingState = useCallCallingState();
  const participants = useParticipants();

  return (
    <div>
      <div>State: {callingState}</div>
      <div>Participants: {participants.length}</div>
    </div>
  );
};
```

**After:**

```tsx {1,3,6-7}
import { useCallStateHooks as getCallStateHooks } from "@stream-io/video-react-sdk";

const { useCallCallingState, useParticipants } = getCallStateHooks();

const MyComponent = () => {
  const callingState = useCallCallingState();
  const participants = useParticipants();

  return (
    <div>
      <div>State: {callingState}</div>
      <div>Participants: {participants.length}</div>
    </div>
  );
};
```

### Why alias to `getCallStateHooks`

Aliasing `useCallStateHooks` to `getCallStateHooks` is required. If you call `useCallStateHooks()` at module scope without aliasing, ESLint will error because functions starting with `use` are treated as hooks and cannot be called outside of components. The alias makes it clear that you're calling a factory function at module scope, not using a hook inside a component.

## Automated Migration with Codemod

Instead of manually migrating your code, you can use the [`@stream-io/video-codemod`](https://www.npmjs.com/package/@stream-io/video-codemod) package to automatically transform your codebase.

### Usage

Run the `use-call-state-hooks` codemod on your codebase:

```bash
npx @stream-io/video-codemod use-call-state-hooks ./src \
  --extensions=ts,tsx \
  --parser=tsx \
  --run-prettier # optional, runs prettier on the transformed files
```

---

For the most recent version of this documentation, visit [https://getstream.io/video/docs/react/v2/advanced/react-compiler/](https://getstream.io/video/docs/react/v2/advanced/react-compiler/).