weakMapMemoize
lruMemoize
需要明确配置缓存大小大于1,并在内部使用LRU缓存。
weakMapMemoize
根据其被调用的参数(在这种情况下,是从你的输入选择器中提取的值)的身份,创建了一个基于 WeakMap
的缓存节点树。这使得 weakMapMemoize
具有实际上无限的缓存大小。只要对参数的引用仍然存在,缓存结果将被保留在内存中,然后在参数被垃圾收集时清除。
设计权衡
使用案例
- 这个记忆器可能最适合需要用许多不同的参数调用同一个选择器实例的情况,例如在列表项组件中使用的单个选择器实例,并用像这样的项目ID调用:
useSelector(state => selectSomeData(state, id))
在使用 weakMapMemoize
之前,你遇到了这样的问题:
- TypeScript
- JavaScript
weakMapMemoize/cacheSizeProblem.ts
import { createSelector } from 'reselect'
export interface RootState {
items: { id: number; category: string; name: string }[]
}
const state: RootState = {
items: [
{ id: 1, category: 'Electronics', name: 'Wireless Headphones' },
{ id: 2, category: 'Books', name: 'The Great Gatsby' },
{ id: 3, category: 'Home Appliances', name: 'Blender' },
{ id: 4, category: 'Stationery', name: 'Sticky Notes' }
]
}
const selectItemsByCategory = createSelector(
[
(state: RootState) => state.items,
(state: RootState, category: string) => category
],
(items, category) => items.filter(item => item.category === category)
)
selectItemsByCategory(state, 'Electronics') // 选择器运行
selectItemsByCategory(state, 'Electronics')
selectItemsByCategory(state, 'Stationery') // 选择器运行
selectItemsByCategory(state, 'Electronics') // 选择器再次运行!
weakMapMemoize/cacheSizeProblem.js
import { createSelector } from 'reselect'
const state = {
items: [
{ id: 1, category: 'Electronics', name: 'Wireless Headphones' },
{ id: 2, category: 'Books', name: 'The Great Gatsby' },
{ id: 3, category: 'Home Appliances', name: 'Blender' },
{ id: 4, category: 'Stationery', name: 'Sticky Notes' }
]
}
const selectItemsByCategory = createSelector(
[state => state.items, (state, category) => category],
(items, category) => items.filter(item => item.category === category)
)
selectItemsByCategory(state, 'Electronics') // 选择器运行
selectItemsByCategory(state, 'Electronics')
selectItemsByCategory(state, 'Stationery') // 选择器运行
selectItemsByCategory(state, 'Electronics') // 选择器再次运行!
在此之前,你可以通过多种不同的方式来解决这个问题:
- 使用
lruMemoize
设置maxSize
:
- TypeScript
- JavaScript
weakMapMemoize/setMaxSize.ts
import { createSelector, lruMemoize } from 'reselect'
import type { RootState } from './cacheSizeProblem'
const selectItemsByCategory = createSelector(
[
(state: RootState) => state.items,
(state: RootState, category: string) => category
],
(items, category) => items.filter(item => item.category === category),
{
memoize: lruMemoize,
memoizeOptions: {
maxSize: 10
}
}
)
weakMapMemoize/setMaxSize.js
import { createSelector, lruMemoize } from 'reselect'
const selectItemsByCategory = createSelector(
[state => state.items, (state, category) => category],
(items, category) => items.filter(item => item.category === category),
{
memoize: lruMemoize,
memoizeOptions: {
maxSize: 10
}
}
)
但这需要提前知道缓存大小。
- 使用
useMemo
创建唯一的选择器实例。
- TypeScript
- JavaScript
weakMapMemoize/withUseMemo.tsx
import type { FC } from 'react'
import { useMemo } from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'
import type { RootState } from './cacheSizeProblem'
const makeSelectItemsByCategory = (category: string) =>
createSelector([(state: RootState) => state.items], items =>
items.filter(item => item.category === category)
)
interface Props {
category: string
}
const MyComponent: FC<Props> = ({ category }) => {
const selectItemsByCategory = useMemo(
() => makeSelectItemsByCategory(category),
[category]
)
const itemsByCategory = useSelector(selectItemsByCategory)
return (
<div>
{itemsByCategory.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
)
}
weakMapMemoize/withUseMemo.jsx
import { useMemo } from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'
const makeSelectItemsByCategory = category =>
createSelector([state => state.items], items =>
items.filter(item => item.category === category)
)
const MyComponent = ({ category }) => {
const selectItemsByCategory = useMemo(
() => makeSelectItemsByCategory(category),
[category]
)
const itemsByCategory = useSelector(selectItemsByCategory)
return (
<div>
{itemsByCategory.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
)
}
- 使用 Re-reselect:
import { createCachedSelector } from 're-reselect'
const selectItemsByCategory = createCachedSelector(
[
(state: RootState) => state.items,
(state: RootState, category: string) => category
],
(items, category) => items.filter(item => item.category === category)
)((state: RootState, category: string) => category)
从5.0.0版本开始,你可以使用 weakMapMemoize
来消除这个问题。
- TypeScript
- JavaScript
weakMapMemoize/cacheSizeSolution.ts
import { createSelector, weakMapMemoize } from 'reselect'
import type { RootState } from './cacheSizeProblem'
const state: RootState = {
items: [
{ id: 1, category: 'Electronics', name: 'Wireless Headphones' },
{ id: 2, category: 'Books', name: 'The Great Gatsby' },
{ id: 3, category: 'Home Appliances', name: 'Blender' },
{ id: 4, category: 'Stationery', name: 'Sticky Notes' }
]
}
const selectItemsByCategory = createSelector(
[
(state: RootState) => state.items,
(state: RootState, category: string) => category
],
(items, category) => items.filter(item => item.category === category),
{
memoize: weakMapMemoize,
argsMemoize: weakMapMemoize
}
)
selectItemsByCategory(state, 'Electronics') // 选择器运行
selectItemsByCategory(state, 'Electronics') // 缓存
selectItemsByCategory(state, 'Stationery') // 选择器运行
selectItemsByCategory(state, 'Electronics') // 仍然缓存!
weakMapMemoize/cacheSizeSolution.js
import { createSelector, weakMapMemoize } from 'reselect'
const state = {
items: [
{ id: 1, category: 'Electronics', name: 'Wireless Headphones' },
{ id: 2, category: 'Books', name: 'The Great Gatsby' },
{ id: 3, category: 'Home Appliances', name: 'Blender' },
{ id: 4, category: 'Stationery', name: 'Sticky Notes' }
]
}
const selectItemsByCategory = createSelector(
[state => state.items, (state, category) => category],
(items, category) => items.filter(item => item.category === category),
{
memoize: weakMapMemoize,
argsMemoize: weakMapMemoize
}
)
selectItemsByCategory(state, 'Electronics') // 选择器运行
selectItemsByCategory(state, 'Electronics') // 缓存
selectItemsByCategory(state, 'Stationery') // 选择器运行
selectItemsByCategory(state, 'Electronics') // 仍然缓存!
这解决了在创建记忆选择器之前需要知道和设置缓存大小的问题。因为 weakMapMemoize
本质上提供了一个开箱即用的动态缓存大小。
参数
名称 | 描述 |
---|---|
func | 需要被记忆的函数。 |
返回
一个带有 .clearCache()
方法的记忆函数。
类型参数
名称 | 描述 |
---|---|
Func | 被记忆的函数的类型。 |