1 | # memoizeOne
|
2 |
|
3 | A memoization library which only remembers the latest invocation
|
4 |
|
5 | [![Build Status](https://travis-ci.org/alexreardon/memoize-one.svg?branch=master)](https://travis-ci.org/alexreardon/memoize-one) [![codecov](https://codecov.io/gh/alexreardon/memoize-one/branch/master/graph/badge.svg)](https://codecov.io/gh/alexreardon/memoize-one) [![dependencies](https://david-dm.org/alexreardon/memoize-one.svg)](https://david-dm.org/alexreardon/memoize-one) [![SemVer](https://img.shields.io/badge/SemVer-2.0.0-brightgreen.svg)](http://semver.org/spec/v2.0.0.html)
|
6 |
|
7 | ## Rationale
|
8 |
|
9 | Cache invalidation is hard:
|
10 |
|
11 | > There are only two hard things in Computer Science: cache invalidation and naming things.
|
12 | >
|
13 | > *Phil Karlton*
|
14 |
|
15 | So keep things simple and just use a cache size of one.
|
16 |
|
17 | Unlike other memoization libraries, `memoizeOne` only remembers the latest arguments and result. No need to worry about cache busting mechanisms such as `maxAge`, `maxSize`, `exlusions` and so on which can be prone to memory leaks. `memoizeOne` simply remembers the last arguments, and if the function is next called with the same arguments then it returns the previous result.
|
18 |
|
19 | ## Usage
|
20 |
|
21 | ### Standard usage
|
22 |
|
23 | ```js
|
24 | import memoizeOne from 'memoize-one';
|
25 |
|
26 | const add = (a, b) => a + b;
|
27 | const memoizedAdd = memoizeOne(add);
|
28 |
|
29 | memoizedAdd(1, 2); // 3
|
30 |
|
31 | memoizedAdd(1, 2); // 3
|
32 | // Add function is not executed: previous result is returned
|
33 |
|
34 | memoizedAdd(2, 3); // 5
|
35 | // Add function is called to get new value
|
36 |
|
37 | memoizedAdd(2, 3); // 5
|
38 | // Add function is not executed: previous result is returned
|
39 |
|
40 | memoizedAdd(1, 2); // 3
|
41 | // Add function is called to get new value.
|
42 | // While this was previously cached,
|
43 | // it is not the latest so the cached result is lost
|
44 | ```
|
45 | [Play with this example](http://www.webpackbin.com/NkCiYkz_M)
|
46 |
|
47 | ### Custom equality function
|
48 | You can also pass in a custom function for checking the equality of two items.
|
49 |
|
50 | ```js
|
51 | import memoizeOne from 'memoize-one';
|
52 | import deepEqual from 'lodash.isEqual';
|
53 |
|
54 | const identity = x => x;
|
55 |
|
56 | const defaultMemoization = memoizeOne(identity);
|
57 | const customMemoization = memoizeOne(identity, deepEqual);
|
58 |
|
59 | const result1 = defaultMemoization({foo: 'bar'});
|
60 | const result2 = defaultMemoization({foo: 'bar'});
|
61 |
|
62 | result1 === result2 // false - difference reference
|
63 |
|
64 | const result3 = customMemoization({foo: 'bar'});
|
65 | const result4 = customMemoization({foo: 'bar'});
|
66 |
|
67 | result3 === result4 // true - arguments are deep equal
|
68 | ```
|
69 | [Play with this example](http://www.webpackbin.com/NJW-tJMdf)
|
70 |
|
71 | #### Type signature
|
72 | Here is the expected [flow](http://flowtype.org) type signature for a custom equality function:
|
73 |
|
74 | ```js
|
75 | type EqualityFn = (a: any, b: any) => boolean;
|
76 | ```
|
77 |
|
78 | ## Installation
|
79 |
|
80 | ```bash
|
81 | # yarn
|
82 | yarn add memoize-one
|
83 |
|
84 | # npm
|
85 | npm install memoize-one --save
|
86 | ```
|
87 |
|
88 | ## Module usage
|
89 |
|
90 | ### ES6 module
|
91 |
|
92 | ```js
|
93 | import memoizeOne from 'memoize-one';
|
94 | ```
|
95 |
|
96 | ### CommonJS
|
97 |
|
98 | If you are in a CommonJS environment (eg [Node](https://nodejs.org)), then **you will need add `.default` to your import**:
|
99 |
|
100 | ```js
|
101 | const memoizeOne = require('memoize-one').default;
|
102 | ```
|
103 |
|
104 | ## `this`
|
105 |
|
106 | ### memoizeOne correctly respects `this` control
|
107 |
|
108 | This library takes special care to maintain, and allow control over the the `this` context for **both** the original function being memoized as well as the returned memoized function. Both the original function and the memoized function's `this` context respect [all the `this` controlling techniques](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md):
|
109 |
|
110 | - new bindings (`new`)
|
111 | - explicit binding (`call`, `apply`, `bind`);
|
112 | - implicit binding (call site: `obj.foo()`);
|
113 | - default binding (`window` or `undefined` in `strict mode`);
|
114 | - fat arrow binding (binding to lexical `this`)
|
115 | - ignored this (pass `null` as `this` to explicit binding)
|
116 |
|
117 | ### Changes to `this` is considered an argument change
|
118 |
|
119 | Changes to the running context (`this`) of a function can result in the function returning a different value event though its arguments have stayed the same:
|
120 |
|
121 | ```js
|
122 | function getA() {
|
123 | return this.a;
|
124 | }
|
125 |
|
126 | const temp1 = {
|
127 | a: 20,
|
128 | };
|
129 | const temp2 = {
|
130 | a: 30,
|
131 | }
|
132 |
|
133 | getA.call(temp1); // 20
|
134 | getA.call(temp2); // 30
|
135 | ```
|
136 |
|
137 | Therefore, in order to prevent against unexpected results, `memoizeOne` takes into account the current execution context (`this`) of the memoized function. If `this` is different to the previous invocation then it is considered a change in argument. [further discussion](https://github.com/alexreardon/memoize-one/issues/3).
|
138 |
|
139 | Generally this will be of no impact if you are not explicity controlling the `this` context of functions you want to memoize with [explicit binding](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md#explicit-binding) or [implicit binding](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md#implicit-binding). `memoizeOne` will detect when you are manipulating `this` and will then consider the `this` context as an argument. If `this` changes, it will re-execute the original function even if the arguments have not changed.
|
140 |
|
141 | ## Performance :rocket:
|
142 |
|
143 | ### Tiny
|
144 | `memoizeOne` is super lightweight at `457 bytes` minified and `299 bytes` gzipped. (`1kb` = `1000 bytes`)
|
145 |
|
146 | ### Extremely fast
|
147 | `memoizeOne` performs better or on par with than other popular memoization libraries for the purpose of remembering the latest invocation.
|
148 |
|
149 | **Results**
|
150 | - [simple arguments](https://www.measurethat.net/Benchmarks/ShowResult/4452)
|
151 | - [complex arguments](https://www.measurethat.net/Benchmarks/ShowResult/4488)
|
152 |
|
153 | The comparisions are not exhaustive and are primiarly to show that `memoizeOne` accomplishes remembering the latest invocation really fast. The benchmarks do not take into account the differences in feature sets, library sizes, parse time, and so on.
|
154 |
|
155 | ## Code health :thumbsup:
|
156 |
|
157 | - Tested with all built in [JavaScript types](https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch1.md).
|
158 | - [100% code coverage](https://codecov.io/gh/alexreardon/memoize-one).
|
159 | - [Continuous integration](https://travis-ci.org/alexreardon/memoize-one) to run tests and type checks.
|
160 | - [`Flow` types](http://flowtype.org) for safer internal execution and external consumption. Also allows for editor autocompletion.
|
161 | - Follows [Semantic versioning (2.0)](http://semver.org/) for safer consumption.
|
162 | - No dependencies |
\ | No newline at end of file |