# Fre's useMemo and useCallback Hooks: A Deep Dive into Memoization

> Master Fre's useMemo and useCallback hooks. Learn how these memoization techniques optimize performance by caching computations and stable function references.

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

---

**Fre provides `useMemo` and `useCallback` hooks that cache expensive computations and stable function references across renders by storing values in a fiber's hook list and comparing dependencies with shallow equality checks.**

Fre is a lightweight UI library that implements a React-like hooks system for functional components. Its memoization hooks—`useMemo` and `useCallback`—allow developers to optimize performance by avoiding unnecessary recalculations and preserving function identity. These hooks are implemented in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts) and rely on Fre's fiber-based architecture to maintain state between renders.

## How Fre's Memoization System Works

### Hook Storage in the Fiber Architecture

Each component render in Fre is represented by a **Fiber**, which maintains a `hooks.list` array to store hook state. The helper function `getSlot<T>(cursor)` in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts) retrieves the current hook's storage slot and returns both the stored value and the fiber instance. At the start of every render, `resetCursor` initializes the cursor to zero, ensuring hooks execute in a deterministic order as the cursor increments with each call.

### The HookMemo Type and Dependency Detection

The core storage structure for memoized values is defined in [`src/type.ts`](https://github.com/frejs/fre/blob/main/src/type.ts) as `type HookMemo<V = any> = [value: V, deps: DependencyList]`. Each slot contains the cached `value` and its `deps` array. To detect changes, Fre uses the `isChanged(a, b)` utility, which performs a shallow comparison using `Object.is` on dependency arrays【src/type.ts†L42-L48】. If any dependency differs, the hook invalidates the cache and recomputes the value.

### useMemo Implementation Details

The `useMemo(cb, deps?)` function, located in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts) at lines 74-84, retrieves its storage slot and checks `isChanged` against previous dependencies【src/hook.ts†L74-L84】. When dependencies have changed—or when `deps` is omitted, causing recomputation on every render—the callback executes to produce a fresh value. Otherwise, Fre returns the cached result from the fiber's hook list, skipping expensive calculations.

### useCallback Implementation Details

`useCallback(cb, deps?)` is implemented in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts) at lines 86-90 as a thin wrapper around `useMemo`【src/hook.ts†L86-L90】. Instead of caching a computed value, it caches the callback function itself, returning a stable reference until dependencies change. This prevents unnecessary function recreation, which is critical when passing callbacks to child components or dependencies to `useEffect` hooks that rely on reference equality.

### Integration with the Render Cycle

When a memoization hook detects changed dependencies and updates its cached value, Fre marks the component's fiber as dirty. This triggers `update(current)` from [`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts) to schedule a re-render, ensuring the component receives the new memoized value or callback.

## Practical Code Examples

### Caching Expensive Calculations with useMemo

```tsx
import { h, useMemo } from 'fre'

function DataProcessor({ dataset }) {
  const processed = useMemo(() => {
    return dataset.filter(item => item.active).sort((a, b) => a.score - b.score)
  }, [dataset])

  return <div>{processed.length} active items</div>
}

```

In this example, `useMemo` stores the filtered and sorted array in a `HookMemo` slot within the fiber. The computation only reruns when the `dataset` reference changes, avoiding O(n log n) operations on every render.

### Stabilizing Function References with useCallback

```tsx
import { h, useCallback, useEffect } from 'fre'

function SearchInput({ onSearch }) {
  const handleKeyUp = useCallback((e) => {
    if (e.key === 'Enter') onSearch(e.target.value)
  }, [onSearch])

  useEffect(() => {
    document.addEventListener('keyup', handleKeyUp)
    return () => document.removeEventListener('keyup', handleKeyUp)
  }, [handleKeyUp])

  return <input type="search" />
}

```

Here, `useCallback` ensures `handleKeyUp` maintains the same reference between renders unless `onSearch` changes. This prevents the `useEffect` cleanup and re-subscription cycle from executing on every render, optimizing event listener management.

### Combining useMemo and useCallback

```tsx
import { h, useMemo, useCallback } from 'fre'

function ItemList({ items, sortFn }) {
  const sortedItems = useMemo(() => [...items].sort(sortFn), [items, sortFn])
  
  const handleItemClick = useCallback((id) => {
    console.log('Clicked item:', id)
  }, [])

  return (
    <ul>
      {sortedItems.map(item => (
        <li key={item.id} onClick={() => handleItemClick(item.id)}>
          {item.name}
        </li>
      ))}
    </ul>
  )
}

```

This pattern demonstrates optimal performance: `sortedItems` recalculates only when data or sorting logic changes, while `handleItemClick` remains a stable reference due to its empty dependency array, preventing unnecessary child component updates.

## Summary

- **Fiber-based storage**: Fre stores memoized values in a `hooks.list` array attached to each component's fiber, accessed via `getSlot` in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts).
- **Shallow dependency checking**: The `isChanged` utility in [`src/type.ts`](https://github.com/frejs/fre/blob/main/src/type.ts) uses `Object.is` comparison to determine when caches invalidate.
- **useMemo**: Caches computed values from expensive calculations, recomputing only when dependencies change or when omitted to force updates every render.
- **useCallback**: A specialized `useMemo` wrapper that caches function references to maintain stable identities across renders.
- **Render integration**: Updated memoized values trigger `update(current)` in [`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts) to schedule necessary re-renders.

## Frequently Asked Questions

### How does Fre's useMemo differ from React's implementation?

Fre's `useMemo` follows the same semantic API as React but implements storage through its fiber architecture rather than React's internal linked list. Both use shallow dependency comparison, but Fre stores values in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts) using the `HookMemo` tuple type defined in [`src/type.ts`](https://github.com/frejs/fre/blob/main/src/type.ts), making the implementation more compact for the library's lightweight footprint.

### What happens if I omit the dependency array in Fre's useMemo or useCallback?

When `deps` is omitted or set to `undefined`, the hook treats this as a signal to recompute on every render. In [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts), the implementation checks `isChanged` against the previous dependencies; if the new `deps` argument is undefined, the comparison fails, causing the callback to execute fresh each time—identical to React's behavior without dependencies.

### Can useCallback prevent child components from re-rendering?

Yes. `useCallback` returns a stable function reference from the fiber's hook storage until dependencies change. When passing this stable reference to child components, Fre's reconciliation process in [`src/reconcile.ts`](https://github.com/frejs/fre/blob/main/src/reconcile.ts) detects unchanged props (via reference equality), potentially skipping unnecessary renders of optimized child components.

### Where are the memoization hooks defined in the Fre source code?

The primary implementations reside in [`src/hook.ts`](https://github.com/frejs/fre/blob/main/src/hook.ts), specifically lines 74-90 for `useMemo` and `useCallback`. Type definitions including `HookMemo` and `isChanged` exist in [`src/type.ts`](https://github.com/frejs/fre/blob/main/src/type.ts) at lines 35-48, while the public API exports these functions from [`src/index.ts`](https://github.com/frejs/fre/blob/main/src/index.ts).