← Back to all hooks

useConstant

Create a stable reference that's initialized once and never changes. Like useRef but with lazy initialization for expensive computations.

Live Demo

See how useConstant only initializes once, while regular objects are recreated on every render.

📊 Render Statistics

Total Renders:1
useConstant Calls:1
Regular Object Calls:1

✅ useConstant Value

Computation Time: 20ms
Result: 21081849486.44
Created: 8:24:08 PM
✨ Initialized once, never changes

❌ Regular Object

Created: 8:24:08 PM
⚠️ Recreated on every render

📝 Event Log

No events yet

💡 Notice:

  • useConstant initializer only runs once (on first render)
  • Regular object is recreated on every render
  • Perfect for expensive computations or stable references
  • Similar to useRef but with lazy initialization

Code Examples

Basic Usage

import { useConstant } from "@uiblock/hooks";

export default function Example() {
  // This expensive computation only runs once
  const config = useConstant(() => {
    console.log('Initializing config...');
    return {
      apiUrl: process.env.API_URL,
      timeout: 5000,
      retries: 3
    };
  });

  return <div>API URL: {config.apiUrl}</div>;
}

Expensive Computation

import { useConstant } from "@uiblock/hooks";

export default function DataProcessor() {
  const processedData = useConstant(() => {
    // This expensive operation only runs once
    const largeDataset = fetchLargeDataset();
    return largeDataset.map(item => ({
      ...item,
      computed: expensiveComputation(item)
    }));
  });

  return <div>{processedData.length} items processed</div>;
}

Stable Event Handlers

import { useConstant } from "@uiblock/hooks";

export default function EventExample() {
  const eventBus = useConstant(() => {
    // Create event bus once
    return new EventEmitter();
  });

  const logger = useConstant(() => {
    // Create logger instance once
    return new Logger({ level: 'debug' });
  });

  return <div>Event bus and logger initialized</div>;
}

vs useRef

import { useConstant } from "@uiblock/hooks";
import { useRef } from "react";

export default function Comparison() {
  // useRef - must initialize with value
  const ref = useRef(expensiveComputation()); // ❌ Runs every render!
  
  // useRef with lazy init pattern
  const ref2 = useRef<ReturnType<typeof expensiveComputation>>();
  if (!ref2.current) {
    ref2.current = expensiveComputation(); // ✅ But verbose
  }
  
  // useConstant - clean and lazy
  const value = useConstant(() => expensiveComputation()); // ✅ Perfect!

  return <div>Value: {value}</div>;
}

How It Works

Here's the implementation:

import { useRef } from 'react'

export function useConstant<T>(fn: () => T): T {
  const ref = useRef<{ value: T }>()

  if (!ref.current) {
    ref.current = { value: fn() }
  }

  return ref.current.value
}

Key Features:

  • Lazy initialization - function only runs once
  • Returns the same reference on every render
  • Cleaner than useRef for initialized values
  • Perfect for expensive computations

API Reference

Parameters

fn: () => T

Initializer function that returns the constant value. Only called once on first render.

Returns

T - The constant value that never changes across renders

💡 Use Cases

  • Expensive computations that should only run once
  • Creating stable class instances (EventEmitter, Logger, etc.)
  • Configuration objects that never change
  • Stable references for third-party libraries
  • Avoiding unnecessary re-computations
  • Cleaner alternative to useRef for initialized values