• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..18-Dec-2021-

dist/H03-May-2022-

es/H03-May-2022-

lib/H03-May-2022-

src/cache/H03-May-2022-5743

LICENSE.mdH A D18-Dec-20211 KiB2016

README.mdH A D18-Dec-202124.5 KiB492345

package.jsonH A D18-Dec-20212.5 KiB9897

README.md

1# Re-reselect
2
3[![Build status][ci-badge]][ci]
4[![Npm version][npm-version-badge]][npm]
5[![Npm downloads][npm-downloads-badge]][npm]
6[![Test coverage report][coveralls-badge]][coveralls]
7
8`re-reselect` is a lightweight wrapper around **[Reselect][reselect]** meant to enhance selectors with **deeper memoization** and **cache management**.
9
10**Switching between different arguments** using standard `reselect` selectors causes **cache invalidation** since default `reselect` cache has a **limit of one**.
11
12`re-reselect` **forwards different calls to different** `reselect` **selectors** stored in cache, so that computed/memoized values are retained.
13
14`re-reselect` **selectors work as normal** `reselect` **selectors** but they are able to determine when **creating a new selector or querying a cached one** on the fly, depending on the supplied arguments.
15
16![Reselect and re-reselect][reselect-and-re-reselect-sketch]
17
18Useful to:
19
20- **Retain selector's cache** when sequentially **called with one/few different arguments** ([example][example-1])
21- **Join similar selectors** into one
22- **Share selectors** with props across multiple component instances (see [reselect example][reselect-sharing-selectors] and [re-reselect solution][example-2])
23- **Instantiate** selectors **on runtime**
24- Enhance `reselect` with [custom caching strategies][cache-objects-docs]
25
26<!-- prettier-ignore -->
27```js
28import createCachedSelector from 're-reselect';
29
30// Normal reselect routine: declare "inputSelectors" and "resultFunc"
31const getUsers = state => state.users;
32const getLibraryId = (state, libName) => state.libraries[libName].id;
33
34const getUsersByLibrary = createCachedSelector(
35  // inputSelectors
36  getUsers,
37  getLibraryId,
38
39  // resultFunc
40  (users, libraryId) => expensiveComputation(users, libraryId),
41)(
42  // re-reselect keySelector
43  // Use "libraryId" as cacheKey
44  (_, libraryId) => libraryId
45);
46
47// Cached selector behave like normal selectors:
48// 2 reselect selectors are created, called and cached
49const reactUsers = getUsersByLibrary(state, 'react');
50const vueUsers = getUsersByLibrary(state, 'vue');
51
52// This 3rd call hits the cache
53const reactUsersAgain = getUsersByLibrary(state, 'react');
54// reactUsers === reactUsersAgain
55// "expensiveComputation" called twice in total
56```
57
58## Table of contents
59
60- [Installation](#installation)
61- [Why? + example](#why--example)
62  - [re-reselect solution](#re-reselect-solution)
63  - [Other viable solutions](#other-viable-solutions)
64- [Examples](#examples)
65- [FAQ](#faq)
66- [API](#api)
67  - [`createCachedSelector`](#createCachedSelector)
68  - [`createStructuredCachedSelector`](#createStructuredCachedSelector)
69  - [keySelector](#keyselector)
70  - [options](#options)
71  - [selector instance][selector-instance-docs]
72- [About re-reselect](#about-re-reselect)
73- [Todo's](#todos)
74- [Contributors](#contributors)
75
76## Installation
77
78```console
79npm install reselect -S
80npm install re-reselect -S
81```
82
83## Why? + example
84
85Let's say `getData` is a `reselect` selector.
86
87```js
88getData(state, itemId, 'dataA');
89getData(state, itemId, 'dataB');
90getData(state, itemId, 'dataC');
91```
92
93The **3rd argument invalidates `reselect` cache** on each call, forcing `getData` to re-evaluate and return a new value.
94
95### re-reselect solution
96
97`re-reselect` selectors keep a **cache of `reselect` selectors** stored by `cacheKey`.
98
99<!-- Please note that part of this lines are repeated in #api chapter -->
100
101`cacheKey` is the return value of the `keySelector` function. It's by default a `string` or `number` but it can be anything depending on the chosen cache strategy (see [cache objects docs][cache-objects-docs]).
102
103`keySelector` is a custom function which:
104
105- takes the same arguments as the final selector (in the example: `state`, `itemId`, `'dataX'`)
106- returns a `cacheKey`
107
108A **unique persisting `reselect` selector instance** stored in cache is used to compute data for a given `cacheKey` (1:1).
109
110Back to the example, `re-reselect` retrieves data by **querying one of the cached selectors** using the 3rd argument as `cacheKey`, allowing cache invalidation only when `state` or `itemId` change (but not `dataType`):
111
112<!-- prettier-ignore -->
113```js
114const getPieceOfData = createCachedSelector(
115  state => state,
116  (_state_, itemId) => itemId,
117  (state, itemId, dataType) => dataType,
118  (state, itemId, dataType) => expensiveComputation(state, itemId, dataType)
119)(
120  (state, itemId, dataType) => dataType // Use dataType as cacheKey
121);
122```
123
124**Replacing a selector with a cached selector is invisible to the consuming application since the API is the same.**
125
126**When a cached selector is called**, the following happens behind the scenes:
127
1281.  **Evaluate the `cacheKey`** for current call by executing `keySelector`
1292.  **Retrieve** from cache the **`reselect` selector** stored under the given `cacheKey`
1303.  **Return found selector or create a new one** if no selector was found
1314.  **Call returned selector** with provided arguments
132
133### Other viable solutions
134
135#### 1- Declare a different selector for each different call
136
137Easy, but doesn't scale. See ["join similar selectors" example][example-1].
138
139#### 2- Declare a `makeGetPieceOfData` selector factory as explained in Reselect docs
140
141The solution suggested in [Reselect docs][reselect-sharing-selectors] is fine, but it has a few downsides:
142
143- Bloats your code by exposing both `get` selectors and `makeGet` selector factories
144- Needs to import/call selector factory instead of directly using selector
145- Two different instances given the same arguments, will individually store and recompute the same result (read [this](https://github.com/reactjs/reselect/pull/213))
146
147#### 3- Wrap your `makeGetPieceOfData` selector factory into a memoizer function and call the returning memoized selector
148
149This is what `re-reselect` actually does. ��
150
151## Examples
152
153- [Join similar selectors][example-1]
154- [Avoid selector factories][example-2]
155- [Cache API calls][example-3]
156- [Programmatic keySelector composition][example-4]
157
158## FAQ
159
160<details>
161  <summary>
162    <b>How do I wrap my existing selector with re-reselect?</b>
163  </summary>
164  <br/>
165
166Given your `reselect` selectors:
167
168  <!-- prettier-ignore -->
169
170```js
171import {createSelector} from 'reselect';
172
173export const getMyData = createSelector(
174  selectorA,
175  selectorB,
176  selectorC,
177  (A, B, C) => doSomethingWith(A, B, C)
178);
179```
180
181...add `keySelector` in the second function call:
182
183  <!-- prettier-ignore -->
184
185```js
186import createCachedSelector from 're-reselect';
187
188export const getMyData = createCachedSelector(
189  selectorA,
190  selectorB,
191  selectorC,
192  (A, B, C) => doSomethingWith(A, B, C)
193)(
194  (state, arg1, arg2) => arg2 // Use arg2 as cacheKey
195);
196```
197
198Voilà, `getMyData` is ready for use!
199
200```js
201const myData = getMyData(state, 'foo', 'bar');
202```
203
204</details>
205
206<details>
207  <summary>
208    <b>How do I use multiple inputs to set the cacheKey?</b>
209  </summary>
210  <br/>
211
212A few good examples and [a bonus](https://github.com/toomuchdesign/re-reselect/issues/3):
213
214<!-- prettier-ignore -->
215```js
216// Basic usage: use a single argument as cacheKey
217createCachedSelector(
218  // ...
219)(
220  (state, arg1, arg2, arg3) => arg3
221)
222
223// Use multiple arguments and chain them into a string
224createCachedSelector(
225  // ...
226)(
227  (state, arg1, arg2, arg3) => `${arg1}:${arg3}`
228)
229
230// Extract properties from an object
231createCachedSelector(
232  // ...
233)(
234  (state, props) => `${props.a}:${props.b}`
235)
236```
237
238</details>
239
240<details>
241  <summary>
242    <b>How do I limit the cache size?</b>
243  </summary>
244  <br/>
245
246Use a [`cacheObject`][cache-objects-docs] which provides that feature by supplying a [`cacheObject` option](#options).
247
248You can also write **your own cache strategy**!
249
250</details>
251
252<details>
253  <summary>
254    <b>How to share a selector across multiple components while passing in props and retaining memoization?</b>
255  </summary>
256  <br/>
257
258[This example][example-2] shows how `re-reselect` would solve the scenario described in [reselect docs][reselect-sharing-selectors].
259Read more about testing selectors on [`reselect` docs][reselect-test-selectors].
260
261</details>
262
263<details>
264  <summary>
265    <b>How do I test a re-reselect selector?</b>
266  </summary>
267  <br/>
268
269Like a normal reselect selector!
270
271`re-reselect` selectors expose the same `reselect` testing methods:
272
273- `dependencies`
274- `resultFunc`
275- `recomputations`
276- `resetRecomputations`
277
278Read more about testing selectors on [`reselect` docs][reselect-test-selectors].
279
280#### Testing `reselect` selectors stored in the cache
281
282Each **re-reselect** selector exposes a `getMatchingSelector` method which returns the **underlying matching selector** instance for the given arguments, **instead of the result**.
283
284`getMatchingSelector` expects the same arguments as a normal selector call **BUT returns the instance of the cached selector itself**.
285
286Once you get a selector instance you can call [its public methods][reselect-selectors-methods].
287
288<!-- prettier-ignore -->
289```js
290import createCachedSelector from 're-reselect';
291
292export const getMyData = createCachedSelector(selectorA, selectorB, (A, B) =>
293  doSomethingWith(A, B)
294)(
295  (state, arg1) => arg1 // cacheKey
296);
297
298// Call your selector
299const myFooData = getMyData(state, 'foo');
300const myBarData = getMyData(state, 'bar');
301
302// Call getMatchingSelector method to retrieve underlying reselect selectors
303// which generated "myFooData" and "myBarData" results
304const myFooDataSelector = getMyData.getMatchingSelector(state, 'foo');
305const myBarDataSelector = getMyData.getMatchingSelector(state, 'bar');
306
307// Call reselect's selectors methods
308myFooDataSelector.recomputations();
309myFooDataSelector.resetRecomputations();
310```
311
312</details>
313
314## API
315
316### createCachedSelector
317
318<!-- prettier-ignore -->
319```js
320import createCachedSelector from 're-reselect';
321
322createCachedSelector(
323  // ...reselect's `createSelector` arguments
324)(
325  keySelector | { options }
326)
327```
328
329Takes the same arguments as reselect's [`createSelector`][reselect-create-selector] and returns a new function which accepts a [`keySelector`](#keyselector) or an [`options`](#options) object.
330
331**Returns** a [selector instance][selector-instance-docs].
332
333### createStructuredCachedSelector
334
335<!-- prettier-ignore -->
336```js
337import { createStructuredCachedSelector } from 're-reselect';
338
339createStructuredCachedSelector(
340  // ...reselect's `createStructuredSelector` arguments
341)(
342  keySelector | { options }
343)
344```
345
346Takes the same arguments as reselect's [`createStructuredSelector`][reselect-create-structured-selector] and returns a new function which accepts a [`keySelector`](#keyselector) or an [`options`](#options) object.
347
348**Returns** a [selector instance][selector-instance-docs].
349
350### keySelector
351
352A custom function receiving the same arguments as your selectors (and `inputSelectors`) and **returning a `cacheKey`**.
353
354`cacheKey` is **by default a `string` or `number`** but can be anything depending on the chosen cache strategy (see [`cacheObject` option](#optionscacheobject)).
355
356The `keySelector` idea comes from [Lodash's .memoize resolver][lodash-memoize].
357
358### options
359
360#### keySelector
361
362Type: `function`<br />
363Default: `undefined`
364
365The [`keySelector`](#keyselector) used by the cached selector.
366
367#### cacheObject
368
369Type: `object`<br />
370Default: [`FlatObjectCache`][cache-objects-docs]
371
372An optional custom **cache strategy object** to handle the caching behaviour. Read more about [re-reselect's custom cache here][cache-objects-docs].
373
374#### keySelectorCreator
375
376Type: `function`<br />
377Default: `undefined`
378
379An optional function with the following signature returning the [`keySelector`](#keyselector) used by the cached selector.
380
381```typescript
382type keySelectorCreator = (selectorInputs: {
383  inputSelectors: InputSelector[];
384  resultFunc: ResultFunc;
385  keySelector: KeySelector;
386}) => KeySelector;
387```
388
389This allows to dynamically **generate `keySelectors` on runtime** based on provided `inputSelectors`/`resultFunc` supporting [**key selectors composition**](https://github.com/toomuchdesign/re-reselect/pull/73). It overrides any provided `keySelector`.
390
391See [programmatic keySelector composition][example-4] example.
392
393#### selectorCreator
394
395Type: `function`<br />
396Default: `reselect`'s [`createSelector`][reselect-create-selector]
397
398An optional function describing a [custom version of createSelector][reselect-create-selector-creator].
399
400### re-reselect selector instance
401
402`createCachedSelector` and `createStructuredCachedSelector` return a **selector instance** which extends the API of a **standard reselect selector**.
403
404> The followings are advanced methods and you won't need them for basic usage!
405
406#### selector`.getMatchingSelector(selectorArguments)`
407
408Retrieve the selector responding to the given arguments.
409
410#### selector`.removeMatchingSelector(selectorArguments)`
411
412Remove from the cache the selector responding to the given arguments.
413
414#### selector`.cache`
415
416Get cacheObject instance being used by the selector (for advanced caching operations like [this](https://github.com/toomuchdesign/re-reselect/issues/40)).
417
418#### selector`.clearCache()`
419
420Clear whole `selector` cache.
421
422#### selector`.dependencies`
423
424Get an array containing the provided `inputSelectors`. Refer to relevant discussion on [Reselect repo][reselect-test-selectors-dependencies].
425
426#### selector`.resultFunc`
427
428Get `resultFunc` for easily [testing composed selectors][reselect-test-selectors].
429
430#### selector`.recomputations()`
431
432Return the number of times the selector's result function has been recomputed.
433
434#### selector`.resetRecomputations()`
435
436Reset `recomputations` count.
437
438#### selector`.keySelector`
439
440Get `keySelector` for utility compositions or testing.
441
442## About re-reselect
443
444- [re-reselect your whole redux state](https://patrickdesjardins.com/blog/re-reselect-your-whole-redux-state)
445- [Understanding reselect and re-reselect](http://alexnitta.com/understanding-reselect-and-re-reselect/)
446- [Advanced Redux patterns: selectors](https://blog.brainsandbeards.com/advanced-redux-patterns-selectors-cb9f88381d74)
447- [Be selective with your state](https://medium.com/riipen-engineering/be-selective-with-your-state-8f1be76cb9f4)
448- [A swift developer’s React Native experience](https://swiftwithjustin.co/2018/06/24/a-swift-developers-react-native-experience)
449- [5 key Redux libraries to improve code reuse](https://blog.logrocket.com/5-redux-libraries-to-improve-code-reuse-9f93eaceaa83)
450- [Rematch's docs](https://github.com/rematch/rematch/blob/1.1.0/plugins/select/README.md#re-reselect)
451- [Redux re-reselect playground](https://codesandbox.io/s/135rwqj2jj)
452
453## Todo's
454
455- Improve TS tests readability
456- More examples
457
458## Contributors
459
460Thanks to you all ([emoji key][docs-all-contributors]):
461
462<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
463<!-- prettier-ignore -->
464<table><tr><td align="center"><a href="http://www.andreacarraro.it"><img src="https://avatars3.githubusercontent.com/u/4573549?v=4" width="100px;" alt="Andrea Carraro"/><br /><sub><b>Andrea Carraro</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/commits?author=toomuchdesign" title="Code">��</a> <a href="https://github.com/toomuchdesign/re-reselect/commits?author=toomuchdesign" title="Documentation">��</a> <a href="#infra-toomuchdesign" title="Infrastructure (Hosting, Build-Tools, etc)">��</a> <a href="https://github.com/toomuchdesign/re-reselect/commits?author=toomuchdesign" title="Tests">⚠️</a> <a href="#review-toomuchdesign" title="Reviewed Pull Requests">��</a></td><td align="center"><a href="https://github.com/xsburg"><img src="https://avatars2.githubusercontent.com/u/830824?v=4" width="100px;" alt="Stepan Burguchev"/><br /><sub><b>Stepan Burguchev</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/commits?author=xsburg" title="Code">��</a> <a href="#ideas-xsburg" title="Ideas, Planning, & Feedback">��</a> <a href="#question-xsburg" title="Answering Questions">��</a> <a href="#review-xsburg" title="Reviewed Pull Requests">��</a> <a href="https://github.com/toomuchdesign/re-reselect/commits?author=xsburg" title="Tests">⚠️</a></td><td align="center"><a href="https://github.com/sgrishchenko"><img src="https://avatars3.githubusercontent.com/u/15995890?v=4" width="100px;" alt="Sergei Grishchenko"/><br /><sub><b>Sergei Grishchenko</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/commits?author=sgrishchenko" title="Code">��</a> <a href="#ideas-sgrishchenko" title="Ideas, Planning, & Feedback">��</a> <a href="https://github.com/toomuchdesign/re-reselect/commits?author=sgrishchenko" title="Tests">⚠️</a> <a href="#tool-sgrishchenko" title="Tools">��</a></td><td align="center"><a href="https://github.com/Andarist"><img src="https://avatars2.githubusercontent.com/u/9800850?v=4" width="100px;" alt="Mateusz Burzyński"/><br /><sub><b>Mateusz Burzyński</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/commits?author=Andarist" title="Code">��</a> <a href="#infra-Andarist" title="Infrastructure (Hosting, Build-Tools, etc)">��</a></td><td align="center"><a href="https://olslash.github.io/"><img src="https://avatars3.githubusercontent.com/u/693493?v=4" width="100px;" alt="Mitch Robb"/><br /><sub><b>Mitch Robb</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/commits?author=olslash" title="Code">��</a> <a href="https://github.com/toomuchdesign/re-reselect/commits?author=olslash" title="Tests">⚠️</a></td><td align="center"><a href="https://github.com/rufman"><img src="https://avatars3.githubusercontent.com/u/1128559?v=4" width="100px;" alt="Stephane Rufer"/><br /><sub><b>Stephane Rufer</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/commits?author=rufman" title="Code">��</a> <a href="https://github.com/toomuchdesign/re-reselect/commits?author=rufman" title="Tests">⚠️</a></td><td align="center"><a href="https://github.com/spiffysparrow"><img src="https://avatars0.githubusercontent.com/u/2788860?v=4" width="100px;" alt="Tracy Mullen"/><br /><sub><b>Tracy Mullen</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/commits?author=spiffysparrow" title="Code">��</a> <a href="https://github.com/toomuchdesign/re-reselect/commits?author=spiffysparrow" title="Tests">⚠️</a></td></tr><tr><td align="center"><a href="https://www.skc.name"><img src="https://avatars1.githubusercontent.com/u/4211838?v=4" width="100px;" alt="Sushain Cherivirala"/><br /><sub><b>Sushain Cherivirala</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/commits?author=sushain97" title="Code">��</a></td><td align="center"><a href="https://twitter.com/MaoStevemao"><img src="https://avatars0.githubusercontent.com/u/6316590?v=4" width="100px;" alt="Steve Mao"/><br /><sub><b>Steve Mao</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/commits?author=stevemao" title="Documentation">��</a></td><td align="center"><a href="https://github.com/Dante-101"><img src="https://avatars2.githubusercontent.com/u/1428826?v=4" width="100px;" alt="Gaurav Lahoti"/><br /><sub><b>Gaurav Lahoti</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/issues?q=author%3ADante-101" title="Bug reports">��</a></td><td align="center"><a href="http://lon.im"><img src="https://avatars3.githubusercontent.com/u/13602053?v=4" width="100px;" alt="Lon"/><br /><sub><b>Lon</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/issues?q=author%3Acnlon" title="Bug reports">��</a></td><td align="center"><a href="https://github.com/bratushka"><img src="https://avatars2.githubusercontent.com/u/5492495?v=4" width="100px;" alt="bratushka"/><br /><sub><b>bratushka</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/commits?author=bratushka" title="Code">��</a></td><td align="center"><a href="https://andrz.me"><img src="https://avatars3.githubusercontent.com/u/615381?v=4" width="100px;" alt="Anders D. Johnson"/><br /><sub><b>Anders D. Johnson</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/commits?author=AndersDJohnson" title="Documentation">��</a></td><td align="center"><a href="https://github.com/wormyy"><img src="https://avatars3.githubusercontent.com/u/8556724?v=4" width="100px;" alt="Július Retzer"/><br /><sub><b>Július Retzer</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/commits?author=wormyy" title="Documentation">��</a></td></tr><tr><td align="center"><a href="https://github.com/maartenschumacher"><img src="https://avatars3.githubusercontent.com/u/10407025?v=4" width="100px;" alt="Maarten Schumacher"/><br /><sub><b>Maarten Schumacher</b></sub></a><br /><a href="#ideas-maartenschumacher" title="Ideas, Planning, & Feedback">��</a></td><td align="center"><a href="https://github.com/alexanderjarvis"><img src="https://avatars2.githubusercontent.com/u/664238?v=4" width="100px;" alt="Alexander Jarvis"/><br /><sub><b>Alexander Jarvis</b></sub></a><br /><a href="#ideas-alexanderjarvis" title="Ideas, Planning, & Feedback">��</a></td><td align="center"><a href="https://github.com/greggb"><img src="https://avatars1.githubusercontent.com/u/514026?v=4" width="100px;" alt="Gregg B"/><br /><sub><b>Gregg B</b></sub></a><br /><a href="#example-greggb" title="Examples">��</a></td><td align="center"><a href="http://ianobermiller.com"><img src="https://avatars0.githubusercontent.com/u/897931?v=4" width="100px;" alt="Ian Obermiller"/><br /><sub><b>Ian Obermiller</b></sub></a><br /><a href="#review-ianobermiller" title="Reviewed Pull Requests">��</a></td><td align="center"><a href="https://github.com/lukyth"><img src="https://avatars3.githubusercontent.com/u/7040242?v=4" width="100px;" alt="Kanitkorn Sujautra"/><br /><sub><b>Kanitkorn Sujautra</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/commits?author=lukyth" title="Documentation">��</a></td><td align="center"><a href="https://github.com/suark"><img src="https://avatars2.githubusercontent.com/u/6233440?v=4" width="100px;" alt="Brian Kraus"/><br /><sub><b>Brian Kraus</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/commits?author=suark" title="Documentation">��</a></td><td align="center"><a href="https://github.com/el-dav"><img src="https://avatars1.githubusercontent.com/u/7252227?v=4" width="100px;" alt="el-dav"/><br /><sub><b>el-dav</b></sub></a><br /><a href="https://github.com/toomuchdesign/re-reselect/issues?q=author%3Ael-dav" title="Bug reports">��</a></td></tr><tr><td align="center"><a href="https://augustin-riedinger.fr"><img src="https://avatars3.githubusercontent.com/u/1970156?v=4" width="100px;" alt="Augustin Riedinger"/><br /><sub><b>Augustin Riedinger</b></sub></a><br /><a href="#ideas-augnustin" title="Ideas, Planning, & Feedback">��</a></td><td align="center"><a href="https://github.com/RichardForrester"><img src="https://avatars0.githubusercontent.com/u/12902182?v=4" width="100px;" alt="RichardForrester"/><br /><sub><b>RichardForrester</b></sub></a><br /><a href="#ideas-RichardForrester" title="Ideas, Planning, & Feedback">��</a></td></tr></table>
465
466<!-- ALL-CONTRIBUTORS-LIST:END -->
467
468[reselect]: https://github.com/reactjs/reselect
469[reselect-sharing-selectors]: https://github.com/reduxjs/reselect/tree/v4.0.0#sharing-selectors-with-props-across-multiple-component-instances
470[reselect-test-selectors]: https://github.com/reactjs/reselect/tree/v4.0.0#q-how-do-i-test-a-selector
471[reselect-test-selectors-dependencies]: https://github.com/reduxjs/reselect/issues/76#issuecomment-299194186
472[reselect-selectors-methods]: https://github.com/reduxjs/reselect/blob/v4.0.0/src/index.js#L81
473[reselect-create-selector]: https://github.com/reactjs/reselect/tree/v4.0.0#createselectorinputselectors--inputselectors-resultfunc
474[reselect-create-structured-selector]: https://github.com/reduxjs/reselect/tree/v4.0.0#createstructuredselectorinputselectors-selectorcreator--createselector
475[reselect-create-selector-creator]: https://github.com/reactjs/reselect/tree/v4.0.0#createselectorcreatormemoize-memoizeoptions
476[lodash-memoize]: https://lodash.com/docs/4.17.4#memoize
477[ci-badge]: https://travis-ci.org/toomuchdesign/re-reselect.svg?branch=master
478[ci]: https://travis-ci.org/toomuchdesign/re-reselect
479[coveralls-badge]: https://coveralls.io/repos/github/toomuchdesign/re-reselect/badge.svg?branch=master
480[coveralls]: https://coveralls.io/github/toomuchdesign/re-reselect?branch=master
481[npm]: https://www.npmjs.com/package/re-reselect
482[npm-version-badge]: https://img.shields.io/npm/v/re-reselect.svg
483[npm-downloads-badge]: https://img.shields.io/npm/dm/re-reselect.svg
484[reselect-and-re-reselect-sketch]: examples/reselect-and-re-reselect.png?raw=true
485[example-1]: examples/1-join-selectors.md
486[example-2]: examples/2-avoid-selector-factories.md
487[example-3]: examples/3-cache-api-calls.md
488[example-4]: examples/4-programmatic-keyselector-composition.md
489[selector-instance-docs]: #re-reselect-selector-instance
490[cache-objects-docs]: src/cache#readme
491[docs-all-contributors]: https://allcontributors.org/docs/en/emoji-key
492