Skip to content

memo

Create a memoized function that caches function results to avoid repeated calculations.

Basic Usage

typescript
import { memo } from 'radash'

const expensiveCalculation = memo((n: number) => {
  console.log('Computing...')
  return n * n * n
})

console.log(expensiveCalculation(5)) // Computing... 125
console.log(expensiveCalculation(5)) // 125 (returned from cache)
console.log(expensiveCalculation(3)) // Computing... 27
console.log(expensiveCalculation(5)) // 125 (returned from cache)

Syntax

typescript
function memo<T extends (...args: any[]) => any>(
  fn: T,
  options?: {
    maxSize?: number
    ttl?: number
  }
): T

Parameters

  • fn (function): The function to memoize
  • options (object, optional): Configuration options
    • maxSize (number, optional): Maximum number of cache entries
    • ttl (number, optional): Time to live for cache entries (milliseconds)

Return Value

Returns a memoized function with the same signature but cached results.

Examples

Basic Memoization

typescript
import { memo } from 'radash'

const fibonacci = memo((n: number): number => {
  console.log(`Computing fibonacci(${n})`)
  if (n <= 1) return n
  return fibonacci(n - 1) + fibonacci(n - 2)
})

console.log(fibonacci(10)) // Compute and cache
console.log(fibonacci(10)) // Return from cache
console.log(fibonacci(5))  // Return from cache

Complex Parameter Memoization

typescript
import { memo } from 'radash'

interface User {
  id: number
  name: string
  age: number
}

const getUserStats = memo((users: User[], filter: string) => {
  console.log('Computing user stats...')
  
  const filtered = users.filter(user => 
    user.name.toLowerCase().includes(filter.toLowerCase())
  )
  
  return {
    count: filtered.length,
    averageAge: filtered.reduce((sum, user) => sum + user.age, 0) / filtered.length,
    names: filtered.map(user => user.name)
  }
})

const users: User[] = [
  { id: 1, name: 'Alice', age: 25 },
  { id: 2, name: 'Bob', age: 30 },
  { id: 3, name: 'Charlie', age: 35 }
]

console.log(getUserStats(users, 'alice')) // Compute
console.log(getUserStats(users, 'alice')) // Return from cache
console.log(getUserStats(users, 'bob'))   // Compute

Async Function Memoization

typescript
import { memo } from 'radash'

const fetchUserData = memo(async (userId: number) => {
  console.log(`Fetching user ${userId}...`)
  const response = await fetch(`/api/users/${userId}`)
  return response.json()
})

// Use memoized async function
async function getUser(userId: number) {
  const user = await fetchUserData(userId)
  console.log('User:', user)
  return user
}

// First call will make request
await getUser(1)
// Second call will return from cache
await getUser(1)

With Cache Size Limit

typescript
import { memo } from 'radash'

const expensiveFunction = memo((input: string) => {
  console.log(`Computing for: ${input}`)
  return input.split('').reverse().join('')
}, { maxSize: 3 })

console.log(expensiveFunction('hello')) // Compute
console.log(expensiveFunction('world')) // Compute
console.log(expensiveFunction('test'))  // Compute
console.log(expensiveFunction('hello')) // Return from cache
console.log(expensiveFunction('new'))   // Compute, will clear oldest cache

With Time to Live Cache

typescript
import { memo } from 'radash'

const getCurrentTime = memo(() => {
  console.log('Getting current time...')
  return new Date().toISOString()
}, { ttl: 5000 }) // 5 second cache

console.log(getCurrentTime()) // Compute
console.log(getCurrentTime()) // Return from cache

// After 5 seconds
setTimeout(() => {
  console.log(getCurrentTime()) // Recompute
}, 6000)

Object Parameter Memoization

typescript
import { memo } from 'radash'

const processConfig = memo((config: Record<string, any>) => {
  console.log('Processing config...')
  return {
    ...config,
    processed: true,
    timestamp: Date.now()
  }
})

const config1 = { theme: 'dark', language: 'en' }
const config2 = { theme: 'light', language: 'zh' }

console.log(processConfig(config1)) // Compute
console.log(processConfig(config1)) // Return from cache
console.log(processConfig(config2)) // Compute

Array Parameter Memoization

typescript
import { memo } from 'radash'

const calculateArrayStats = memo((numbers: number[]) => {
  console.log('Calculating array stats...')
  return {
    sum: numbers.reduce((acc, num) => acc + num, 0),
    average: numbers.reduce((acc, num) => acc + num, 0) / numbers.length,
    min: Math.min(...numbers),
    max: Math.max(...numbers)
  }
})

const numbers1 = [1, 2, 3, 4, 5]
const numbers2 = [6, 7, 8, 9, 10]

console.log(calculateArrayStats(numbers1)) // Compute
console.log(calculateArrayStats(numbers1)) // Return from cache
console.log(calculateArrayStats(numbers2)) // Compute

Multi-Parameter Memoization

typescript
import { memo } from 'radash'

const complexCalculation = memo((a: number, b: number, operation: string) => {
  console.log(`Computing ${a} ${operation} ${b}...`)
  
  switch (operation) {
    case 'add':
      return a + b
    case 'multiply':
      return a * b
    case 'divide':
      return a / b
    default:
      return a - b
  }
})

console.log(complexCalculation(5, 3, 'add'))      // Compute
console.log(complexCalculation(5, 3, 'add'))      // Return from cache
console.log(complexCalculation(5, 3, 'multiply')) // Compute

Recursive Function Memoization

typescript
import { memo } from 'radash'

const factorial = memo((n: number): number => {
  console.log(`Computing factorial(${n})`)
  if (n <= 1) return 1
  return n * factorial(n - 1)
})

console.log(factorial(5)) // Compute
console.log(factorial(5)) // Return from cache
console.log(factorial(3)) // Return from cache (because factorial(3) was computed when calculating factorial(5))

Conditional Memoization

typescript
import { memo } from 'radash'

const conditionalMemo = memo((value: string, shouldCache: boolean) => {
  console.log(`Processing: ${value}`)
  return value.toUpperCase()
})

// Decide whether to use cache based on condition
console.log(conditionalMemo('hello', true))  // Compute and cache
console.log(conditionalMemo('hello', true))  // Return from cache
console.log(conditionalMemo('hello', false)) // Compute (different parameters)

Cache Cleanup

typescript
import { memo } from 'radash'

const cachedFunction = memo((input: string) => {
  console.log(`Processing: ${input}`)
  return input.length
}, { maxSize: 2 })

console.log(cachedFunction('hello')) // Compute
console.log(cachedFunction('world')) // Compute
console.log(cachedFunction('test'))  // Compute, will clear 'hello' cache
console.log(cachedFunction('hello')) // Recompute

Performance Optimization Example

typescript
import { memo } from 'radash'

// Simulate expensive calculation
const expensiveOperation = memo((data: number[]) => {
  console.log('Performing expensive operation...')
  
  // Simulate time-consuming calculation
  let result = 0
  for (let i = 0; i < 1000000; i++) {
    result += data.reduce((sum, num) => sum + num, 0)
  }
  
  return result
})

const data1 = [1, 2, 3, 4, 5]
const data2 = [6, 7, 8, 9, 10]

console.time('First call')
console.log(expensiveOperation(data1))
console.timeEnd('First call')

console.time('Cached call')
console.log(expensiveOperation(data1))
console.timeEnd('Cached call')

console.time('New data call')
console.log(expensiveOperation(data2))
console.timeEnd('New data call')

Error Handling

typescript
import { memo } from 'radash'

const riskyFunction = memo((input: string) => {
  console.log(`Processing: ${input}`)
  
  if (input === 'error') {
    throw new Error('Simulated error')
  }
  
  return input.toUpperCase()
})

try {
  console.log(riskyFunction('hello')) // Works normally
  console.log(riskyFunction('hello')) // Return from cache
  console.log(riskyFunction('error')) // Throws error
} catch (error) {
  console.error('Error:', error.message)
}

Notes

  1. Memory usage: Cache consumes memory, set appropriate maxSize
  2. Parameter comparison: Uses strict equality for parameter comparison
  3. Async functions: Supports async functions, but caches Promises
  4. Side effects: Avoid side effects in memoized functions
  5. Cache invalidation: Use ttl to set cache expiration time

Differences from Other Methods

  • memo(): Memoization tool provided by radash
  • Manual caching: Requires implementing cache logic yourself
  • useMemo(): React Hook, only for use within components

Practical Application Scenarios

  1. Mathematical calculations: Cache expensive mathematical operations
  2. API calls: Cache API responses
  3. Data processing: Cache data processing results
  4. Recursive functions: Optimize recursive algorithm performance
  5. Configuration processing: Cache configuration calculation results

Released under the MIT License.