# State Management Hooks in Fre: useState and useReducer Explained

> Discover Frejs state management hooks useState and useReducer. Learn how to manage simple and complex component states effectively with these React-compatible tools.

- Repository: [frejs/fre](https://github.com/frejs/fre)
- Tags: deep-dive
- Published: 2026-03-02

---

**Fre provides React-compatible state management hooks including `useState` for simple state and `useReducer` for complex logic, both implemented in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts) and sharing a unified update mechanism.**

Fre is a lightweight alternative to React that implements familiar hooks for managing component state. The library exposes two primary state management hooks—**`useState`** and **`useReducer`**—which handle everything from simple counters to Redux-style state machines. These hooks are defined in the core hook module and exported through the public API for direct consumption.

## Available State Management Hooks

Fre offers two complementary hooks for state management, each serving different complexity needs while sharing the same underlying architecture.

### useState for Simple State

The **`useState`** hook provides a straightforward way to add reactive state to functional components. It accepts an initial value (or a lazy initializer function) and returns a tuple containing the current state and a setter function.

According to the Fre source code in [`src/hook.ts:L25-L27`](https://github.com/frejs/fre/blob/master/src/hook.ts#L25-L27), `useState` is implemented as a thin wrapper around `useReducer`, passing `null` as the reducer argument. This design ensures consistency across all state updates while keeping the API surface minimal for simple use cases.

### useReducer for Complex Logic

The **`useReducer`** hook handles more structured state transitions through a reducer function, following the Redux pattern. It accepts a reducer function and an initial state (or initializer), returning the current state and a dispatch function.

The implementation spans lines [`src/hook.ts:L29-L51`](https://github.com/frejs/fre/blob/master/src/hook.ts#L29-L51), where the hook manages state transitions by calling the provided reducer with the previous state and the dispatched action. When no reducer is supplied (the `useState` case), the logic falls back to direct value assignment or functional updates.

## Internal Implementation Details

Both hooks rely on a shared internal mechanism that manages hook instances through a slot-based system tied to the fiber architecture.

### Slot Allocation via getSlot

Every hook call reserves a position in the current fiber’s `hooks.list` using a global cursor. The `getSlot` function at [`src/hook.ts:L97-L104`](https://github.com/frejs/fre/blob/master/src/hook.ts#L97-L104) handles this allocation, ensuring that each hook maintains its identity across re-renders. On the initial render, the slot is empty; subsequent renders retrieve the existing state from the fiber’s hook list.

### The Update Mechanism

State updates trigger re-renders through a unified dispatch flow. When you call the setter (from `useState`) or dispatch (from `useReducer`), the internal logic at [`src/hook.ts:L43-L48`](https://github.com/frejs/fre/blob/master/src/hook.ts#L43-L48) computes the next state:

- If a reducer exists, it invokes `reducer(previous, action)`
- If no reducer exists (useState), it checks if the value is a function (`isFn(value)`) and calls it with the previous state, or uses the value directly
- When the new value differs from the current state, `update(current)` schedules a re-render of the component fiber

## Usage Examples

### Basic useState Counter

Import `useState` from the Fre package to manage simple scalar values:

```typescript
import { h, render, useState } from 'fre';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <button onClick={() => setCount(c => c + 1)}>
      Count: {count}
    </button>
  );
}

render(<Counter />, document.getElementById('app'));

```

This example demonstrates lazy functional updates using `c => c + 1`, which Fre handles internally by detecting function values at [`src/hook.ts:L43-L48`](https://github.com/frejs/fre/blob/master/src/hook.ts#L43-L48).

### Complex State with useReducer

For structured state logic, import `useReducer` and define your state transitions:

```typescript
import { h, render, useReducer } from 'fre';

type Action = { type: 'increment' } | { type: 'decrement' };

function counterReducer(state: number, action: Action): number {
  switch (action.type) {
    case 'increment': return state + 1;
    case 'decrement': return state - 1;
    default: return state;
  }
}

function Counter() {
  const [count, dispatch] = useReducer(counterReducer, 0);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
    </div>
  );
}

```

The dispatch function triggers the same internal `update(current)` call used by `useState`, ensuring consistent re-render behavior across both hooks.

### Public API Export

Both hooks are publicly available through the main entry point. As defined in [`src/index.ts:L4-L6`](https://github.com/frejs/fre/blob/master/src/index.ts#L4-L6), you can import them directly:

```typescript
import { useState, useReducer } from 'fre';

```

Example implementations are also available in the demo directory at [[`demo/src/use-state.tsx`](https://github.com/frejs/fre/blob/main/demo/src/use-state.tsx)](https://github.com/frejs/fre/blob/master/demo/src/use-state.tsx) and [[`demo/src/use-reducer.tsx`](https://github.com/frejs/fre/blob/main/demo/src/use-reducer.tsx)](https://github.com/frejs/fre/blob/master/demo/src/use-reducer.tsx).

## Summary

- Fre implements **`useState`** and **`useReducer`** in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts) as the primary state management hooks, exported through [`src/index.ts`](https://github.com/frejs/fre/blob/main/src/index.ts).
- **`useState`** is a specialized wrapper around `useReducer` that passes `null` as the reducer, delegating to the same internal update logic.
- Both hooks use a slot-based allocation system via `getSlot` to maintain state across re-renders within the fiber architecture.
- State updates schedule re-renders through the `update(current)` mechanism when values change, as implemented at lines 43-48 of the hook module.
- The API supports both direct value updates and functional updates (`prev => next`), with lazy initialization supported for both hooks.

## Frequently Asked Questions

### What is the difference between useState and useReducer in Fre?

**`useState`** is optimized for simple scalar values and direct updates, while **`useReducer`** manages complex state objects through action-based transitions. In the Fre codebase, `useState` actually calls `useReducer` internally with a `null` reducer, meaning they share identical performance characteristics and update mechanisms—the difference is purely API ergonomics.

### How does Fre implement useState internally?

According to [`src/hook.ts:L25-L27`](https://github.com/frejs/fre/blob/master/src/hook.ts#L25-L27), `useState` invokes `useReducer` with `null` as the first argument. This design keeps the codebase DRY, ensuring that both hooks benefit from the same slot allocation and comparison logic defined in the shared implementation.

### Can I use lazy initialization with Fre's state hooks?

Yes, both hooks support lazy initialization. Pass a function instead of a value to defer expensive computations until the initial render. The initializer runs only once when the slot is first allocated in the fiber's `hooks.list`, as handled by the slot creation logic at [`src/hook.ts:L97-L104`](https://github.com/frejs/fre/blob/master/src/hook.ts#L97-L104).

### Where are the state management hooks exported from in Fre?

The hooks are defined in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts) and re-exported from the public API entry point at [`src/index.ts:L4-L6`](https://github.com/frejs/fre/blob/master/src/index.ts#L4-L6). Import them directly from the `fre` package: `import { useState, useReducer } from 'fre'`.