use-memo-one review
use-memo-one 1.1.3 provides `useMemoOne` and `useCallbackOne`, two React hooks that retain the latest cached reference until their component becomes unreachable. React documents its own `useMemo` and `useCallback` caches as performance hints that may be discarded; this package makes stable identity a semantic promise while dependencies remain strictly equal. Version 1.1.3 added React 18 to the peer range and moved CI to GitHub Actions. It does not deep-compare dependencies, fix stale closures, or support React 19 in its declared peer range.
use-memo-one 1.1.3 installed in 0.9 seconds with 4 packages and 1 MB in our sandbox, bundled to 3.1 KB gzipped, and had 0 audit findings. Install it only when stable cache identity is application behavior on React 16.8 through 18; ordinary optimization and React 19 code should stay with React's built-ins.
We installed it
| Install | ✓ · 0.9s | 4 packages on disk · 1 MB |
| Import | ✓ | ESM import works · require() works · CommonJS package |
| Browser | 3.1 KB | gzipped (7.8 KB minified), bundled with esbuild |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does use-memo-one install cleanly?
Yes. In a fresh container with an empty cache, npm install use-memo-one finished in 0.9s, leaving 4 packages and 1 MB on disk. npm audit reported no known vulnerabilities.
How much does use-memo-one add to a browser bundle?
3.1 KB gzipped (7.8 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.
Does use-memo-one work with both ESM and CommonJS?
Yes. Both import 'use-memo-one' and require('use-memo-one') worked in Node 22 in our run. The package is published as CommonJS.
Does use-memo-one include TypeScript types?
Yes, type declarations ship inside the package, so no @types install is needed.
use-memo-one or react: which should you use?
react: Use built-in useMemo and useCallback when caching is an optimization or the application runs React 19. use-memo-one 1.1.3 installed in 0.9 seconds with 4 packages and 1 MB in our sandbox, bundled to 3.1 KB gzipped, and had 0 audit findings.
When should you not use use-memo-one?
Your app uses React 19. Version 1.1.3 declares only React 16.8, 17, and 18, so strict peer resolution can reject the install.
Use it if
- A third-party component or subscription treats object or callback reference identity as observable behavior.
- React 16.8 through 18 code needs a drop-in memo or callback API with a retained last cache.
- The extra retained memory is understood and every captured dependency can be listed correctly.
- A legacy library already depends on the package's alias exports and stable-reference guarantee.
- Your app uses React 19. Version 1.1.3 declares only React 16.8, 17, and 18, so strict peer resolution can reject the install.
- Memoization is only a speed optimization. React's built-in hooks avoid another package and are the APIs current React documentation teaches.
- Mounted-tree memory retention is a concern. The README says this cache cannot be released until the component can be garbage-collected.
- You expect structural comparison. The implementation checks dependency entries by strict equality, so a new object or function invalidates the cache.
- You need active React compatibility work. npm dates 1.1.3 to August 2022, and GitHub shows no repository push after December 2022.
Setup reality
We installed use-memo-one 1.1.3 in 0.9 seconds in our fresh Node 22 sandbox. It left 4 packages and 1 MB on disk, and npm audit reported 0 known vulnerabilities. The package has 0 direct dependencies, 1 React peer, 60 KB unpacked, an MIT license, and bundled TypeScript declarations. Its CommonJS package has no exports map; both require() and ESM import worked.
The peer range is ^16.8.0 || ^17.0.0 || ^18.0.0. Version 1.1.3's release change was adding React 18, so React 19 remains outside the published contract. Four names are exported: useMemoOne, useCallbackOne, plus aliases named useMemo and useCallback. The aliases help eslint-plugin-react-hooks recognize dependency arrays, but they collide if the same file imports those names from React. No provider or configuration file is required.
Dependency comparison is shallow, positional, and based on strict equality. Inline objects, arrays, and functions change on every render unless created inside the factory or stabilized first. Omitting the dependency list recomputes on every render; an empty list keeps the initial value. That retained callback can capture the first props forever if a dependency is missing. Factories still execute during rendering and must be pure under React development checks.
Our browser bundle measured 7.8 KB minified and 3.1 KB gzipped for a full-package import. The code cost is small, but the semantic promise also retains the latest cached value until component garbage collection. Most components should use React's hooks and remain correct if a cache is forgotten. Reserve use-memo-one for integrations where reference identity itself is required behavior, then test it under the exact React version because upstream peer support stops at 18.
Patterns
Retain one derived object memoize-derived-object
import { useMemoOne } from 'use-memo-one';
function Profile({ name, role }) {
const summary = useMemoOne(() => ({ name, role }), [name, role]);
return <ProfileCard summary={summary} />;
}The same object is returned while both dependencies are strictly equal; changing either dependency creates a new object.
Retain a callback until inputs change memoize-callback
import { useCallbackOne } from 'use-memo-one';
function SaveButton({ documentId, save }) {
const onSave = useCallbackOne(() => save(documentId), [save, documentId]);
return <button onClick={onSave}>Save</button>;
}Include every value closed over by the callback. A stable function reference still holds stale data when one is omitted.
Use aliases recognized by hook linting use-lint-friendly-aliases
import { useMemo, useCallback } from 'use-memo-one';
const options = useMemo(() => ({ roomId }), [roomId]);
const connect = useCallback(() => openRoom(roomId), [roomId]);Do not import the same 2 names from React in this file; the README warns that the bindings collide.
Stabilize a context value memoize-context-value
function SessionProvider({ user, signOut, children }) {
const value = useMemoOne(() => ({ user, signOut }), [user, signOut]);
return <SessionContext.Provider value={value}>{children}</SessionContext.Provider>;
}Consumers still rerender when `user` or `signOut` genuinely changes; this prevents only avoidable identity replacement.
Pass a stable object to a memo child support-memoized-child
const Chart = memo(({ config }) => <ChartCanvas config={config} />);
function Dashboard({ theme, limit }) {
const config = useMemoOne(() => ({ theme, limit }), [theme, limit]);
return <Chart config={config} />;
}Reference stability matters here only because `Chart` is memoized or otherwise compares the `config` identity.
Keep an effect options object stable stabilize-effect-dependency
function Room({ roomId, serverUrl }) {
const options = useMemoOne(() => ({ roomId, serverUrl }), [roomId, serverUrl]);
useEffect(() => {
const connection = connect(options);
return () => connection.close();
}, [options]);
}Create effect-only objects inside the effect when possible; use this form when other code also consumes the stable object.
Retain one mounted-component value cache-for-component-lifetime
function Editor() {
const model = useMemoOne(() => createEditorModel(), []);
return <EditorView model={model} />;
}An empty list retains the initial result while mounted. The factory must stay pure because it executes during render.
Recompute when the list is omitted recompute-every-render
function Clock() {
const snapshot = useMemoOne(() => ({ renderedAt: Date.now() }));
return <time>{snapshot.renderedAt}</time>;
}Repository tests show that no second argument means no cross-render memoization; pass `[]` only when the first result should persist.
Depend on stable primitives avoid-unstable-dependencies
const rows = useMemoOne(
() => selectRows(data, { status, owner }),
[data, status, owner]
);Version 1.1.3 compares each dependency with strict equality and does no deep or structural comparison.
Preserve a typed callback signature type-callback-parameters
function Search({ runSearch }: { runSearch: (query: string) => void }) {
const submit = useCallbackOne((query: string) => {
runSearch(query.trim());
}, [runSearch]);
return <SearchBox onSubmit={submit} />;
}Bundled declarations retain the callback function type, but the package's React peer range still ends at version 18.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| react | npm | Use built-in `useMemo` and `useCallback` when caching is an optimization or the application runs React 19. |
| memoize-one | npm | Use it to retain the latest call of an ordinary function outside React's hook lifecycle. |
| use-deep-compare | npm | Use it only when hook dependencies truly need structural comparison and the added comparison cost is justified. |
More web frontend guides
postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

