CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-moize

Blazing fast memoization library for JavaScript with comprehensive configuration options and React support

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

specialized-memoization.mddocs/

Specialized Memoization

Specialized memoization methods optimized for specific use cases including promises, React components, and argument serialization.

Capabilities

Promise Memoization

Specialized memoization for async functions and promises with automatic resolution caching.

/**
 * Promise/async function memoization with automatic resolution caching
 * Sets isPromise: true and updateExpire: true by default
 * @returns Moizer optimized for promise-based functions
 */
promise: Moizer<{ isPromise: true }>;

Usage Examples:

import moize from "moize";

// API call memoization
const fetchUser = async (userId: string) => {
  const response = await fetch(`/api/users/${userId}`);
  if (!response.ok) throw new Error(`Failed to fetch user ${userId}`);
  return response.json();
};

const memoizedFetchUser = moize.promise(fetchUser);

// First call - makes HTTP request
const user1 = await memoizedFetchUser("123");

// Second call - returns cached promise result
const user2 = await memoizedFetchUser("123");

// Database query memoization
const getUserFromDB = async (id: number) => {
  const result = await db.query('SELECT * FROM users WHERE id = ?', [id]);
  return result[0];
};

const memoizedDBQuery = moize.promise.maxAge(30000)(getUserFromDB);

// Combining with other options
const advancedPromiseMemoization = moize.promise
  .maxSize(50)
  .maxAge(60000)
  .profile('api-calls')(fetchUser);

React Component Memoization

Specialized memoization for React components with props-based caching.

/**
 * React component memoization based on props comparison
 * Creates component wrapper that memoizes based on props and context
 * @returns Moizer optimized for React components
 */
react: Moizer<{ isReact: true }>;

Usage Examples:

import moize from "moize";
import React from "react";

// Basic component memoization
interface UserCardProps {
  user: {
    id: number;
    name: string;
    email: string;
  };
  theme: 'light' | 'dark';
}

const UserCard: React.FC<UserCardProps> = ({ user, theme }) => (
  <div className={`user-card user-card--${theme}`}>
    <h3>{user.name}</h3>
    <p>{user.email}</p>
  </div>
);

const MemoizedUserCard = moize.react(UserCard);

// Component with deep props comparison
const ComplexComponent: React.FC<{
  data: { items: any[]; metadata: Record<string, any> };
  config: { showDetails: boolean; sortBy: string };
}> = ({ data, config }) => {
  // Complex rendering logic
  return <div>{/* rendered content */}</div>;
};

const MemoizedComplexComponent = moize.react.deep(ComplexComponent);

// With additional configuration
const OptimizedComponent = moize.react
  .maxSize(10)  // Keep 10 different prop combinations
  .shallow      // Use shallow comparison for props
  (UserCard);

// Functional component with hooks
const CounterComponent: React.FC<{ initialCount: number }> = ({ initialCount }) => {
  const [count, setCount] = React.useState(initialCount);
  
  return (
    <div>
      <span>Count: {count}</span>
      <button onClick={() => setCount(c => c + 1)}>Increment</button>
    </div>
  );
};

const MemoizedCounter = moize.react(CounterComponent);

useMoize Hook Pattern

Custom React hook pattern for using moize within functional components with hooks.

/**
 * Custom hook pattern for memoization within React functional components
 * Not a built-in export - user-implemented pattern from the documentation
 */
function useMoize<T extends (...args: any[]) => any>(
  fn: T,
  args: Parameters<T>,
  options?: Options<T>
): ReturnType<T>;

Implementation Example:

import { useRef } from 'react';
import moize from 'moize';

export function useMoize(fn, args, options) {
    const moizedFnRef = useRef(moize(fn, options));

    return moizedFnRef.current(...args);
}

Usage Examples:

import React from 'react';
import { useMoize } from './moize-hooks';

function MyComponent({ first, second, object }) {
    // Standard usage
    const sum = useMoize((a, b) => a + b, [first, second]);
    
    // With options
    const deepSum = useMoize((obj) => obj.a + obj.b, [object], {
        isDeepEqual: true,
    });

    return (
        <div>
            Sum of {first} and {second} is {sum}. Sum of {object.a} and{' '}
            {object.b} is {deepSum}.
        </div>
    );
}

// Advanced usage with complex computations
function DataProcessingComponent({ data, filters, sortBy }) {
    // Expensive data processing with memoization
    const processedData = useMoize(
        (items, filterConfig, sort) => {
            return items
                .filter(item => filterConfig.active ? item.status === 'active' : true)
                .filter(item => filterConfig.category ? item.category === filterConfig.category : true)
                .sort((a, b) => sort === 'name' ? a.name.localeCompare(b.name) : a.id - b.id);
        },
        [data, filters, sortBy],
        {
            isDeepEqual: true,
            maxSize: 10,
            profileName: 'data-processing'
        }
    );

    // Complex calculation with custom equality
    const aggregatedStats = useMoize(
        (items) => ({
            total: items.length,
            active: items.filter(item => item.status === 'active').length,
            categories: [...new Set(items.map(item => item.category))].length,
            avgValue: items.reduce((sum, item) => sum + item.value, 0) / items.length
        }),
        [processedData],
        {
            maxAge: 30000, // 30 second TTL
            isShallowEqual: true
        }
    );

    return (
        <div>
            <h3>Processed Data ({processedData.length} items)</h3>
            <div>Stats: {aggregatedStats.active} active, {aggregatedStats.categories} categories</div>
            {/* Render processed data */}
        </div>
    );
}

Serialization-based Memoization

Memoization using argument serialization for complex cache key generation.

/**
 * Serialization-based memoization using default JSON serialization
 * Converts arguments to strings for cache key comparison
 * @returns Moizer with serialization enabled
 */
serialize: Moizer<{ isSerialized: true }>;

/**
 * Serialization-based memoization with custom serializer
 * @param serializer Custom function to serialize cache keys
 * @returns Moizer with custom serialization
 */
serializeWith<Serializer extends Serialize>(
  serializer: Serializer
): Moizer<{ isSerialized: true; serializer: Serializer }>;

type Serialize = (key: Key) => string[];
type Key<Arg extends any = any> = Arg[];

Usage Examples:

import moize from "moize";

// Default JSON serialization
const processComplexObject = (obj: { 
  data: any[]; 
  options: Record<string, any>; 
}) => {
  return obj.data.map(item => ({ ...item, ...obj.options }));
};

const serializedMemoized = moize.serialize(processComplexObject);

const obj1 = { data: [{ id: 1 }], options: { active: true } };
const obj2 = { data: [{ id: 1 }], options: { active: true } }; // Different objects, same content

console.log(serializedMemoized(obj1)); // Computed
console.log(serializedMemoized(obj2)); // Cached (serialization matches)

// Custom serialization
const customSerializer = (key: any[]) => {
  return key.map(arg => {
    if (typeof arg === 'object' && arg !== null) {
      // Sort object keys before stringifying for consistent serialization
      const sortedObj = Object.keys(arg)
        .sort()
        .reduce((result, key) => {
          result[key] = arg[key];
          return result;
        }, {} as any);
      return JSON.stringify(sortedObj);
    }
    return String(arg);
  });
};

const customSerializedMemoized = moize.serializeWith(customSerializer)(processComplexObject);

// Serialization with other options
const advancedSerialized = moize.serialize
  .maxSize(20)
  .maxAge(45000)
  .profile('serialized-operations')(processComplexObject);

// Case-insensitive string serialization
const caseInsensitiveSerializer = (key: any[]) => {
  return key.map(arg => {
    if (typeof arg === 'string') {
      return arg.toLowerCase();
    }
    return typeof arg === 'object' ? JSON.stringify(arg) : String(arg);
  });
};

const textProcessor = (text: string, options: { trim: boolean }) => {
  return options.trim ? text.trim().toUpperCase() : text.toUpperCase();
};

const caseInsensitiveMemoized = moize.serializeWith(caseInsensitiveSerializer)(textProcessor);

console.log(caseInsensitiveMemoized("Hello", { trim: true })); // Computed
console.log(caseInsensitiveMemoized("HELLO", { trim: true })); // Cached (case-insensitive)

Combining Specialized Methods

Specialized methods can be combined with other moize features for comprehensive optimization.

import moize from "moize";

// Promise memoization with size and TTL limits
const apiCall = async (endpoint: string, params: Record<string, any>) => {
  const url = new URL(endpoint);
  Object.entries(params).forEach(([key, value]) => {
    url.searchParams.append(key, String(value));
  });
  
  const response = await fetch(url.toString());
  return response.json();
};

const optimizedApiCall = moize.promise
  .serialize        // Handle complex params objects
  .maxSize(100)     // Cache up to 100 different calls
  .maxAge(300000)   // 5 minute TTL
  .profile('api')   // Monitor performance
  (apiCall);

// React component with deep comparison and size limit
const DataVisualization: React.FC<{
  data: Array<{ id: string; values: number[] }>;
  config: { 
    chartType: 'bar' | 'line' | 'pie';
    colors: string[];
    showLegend: boolean;
  };
}> = ({ data, config }) => {
  // Complex chart rendering
  return <div>{/* chart content */}</div>;
};

const MemoizedVisualization = moize.react
  .deep           // Deep comparison for data and config
  .maxSize(5)     // Keep 5 different data/config combinations
  (DataVisualization);

Performance Considerations

Different specialized methods have different performance characteristics:

  • Promise memoization: Best for async operations with network calls or heavy computations
  • React memoization: Optimizes component re-rendering, especially with complex props
  • Serialization: Adds overhead but enables complex object comparison without deep equality

docs

argument-transformation.md

cache-introspection.md

cache-management.md

core-memoization.md

equality-comparison.md

index.md

specialized-memoization.md

statistics-profiling.md

utility-methods.md

tile.json