re-reselect
Advanced tools
Comparing version 0.4.0 to 0.5.0
@@ -0,1 +1,4 @@ | ||
# 0.5.0 | ||
- Expose `resultFunc` attribute | ||
# 0.4.0 | ||
@@ -2,0 +5,0 @@ - Expose `removeMatchingSelector` method |
@@ -60,2 +60,4 @@ (function (global, factory) { | ||
selector.resultFunc = funcs[funcs.length - 1]; | ||
return selector; | ||
@@ -62,0 +64,0 @@ }; |
@@ -45,4 +45,6 @@ import { createSelector } from 'reselect'; | ||
selector.resultFunc = funcs[funcs.length - 1]; | ||
return selector; | ||
}; | ||
} |
@@ -50,4 +50,6 @@ 'use strict'; | ||
selector.resultFunc = funcs[funcs.length - 1]; | ||
return selector; | ||
}; | ||
} |
{ | ||
"name": "re-reselect", | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"description": "Memoize selectors and avoid recalculation between calls with different inputs", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
170
README.md
# Re-reselect [![Build Status][ci-img]][ci] | ||
Improve **[Reselect][reselect] performance** on few edge cases, by initializing selectors on the fly, using a **memoized factory**. | ||
Improve **[Reselect][reselect] selectors performance** on a few edge cases, by initializing selectors on runtime, using a **memoized factory**. | ||
The resulting selector acts like a normal one, but It's able to determine when **querying a new selector instance or a cached one** on the fly, depending on the supplied arguments. | ||
**Re-reselect returns a reselect-like selector**, which is able to determine internally when **querying a new selector instance or a cached one** on the fly, depending on the supplied arguments. | ||
Useful to **reduce selectors recalculation** when: | ||
- the same selector is repeatedly **called with one/few different arguments** | ||
- the same selector is **imported by different modules** at the same time | ||
- a selector is sequentially **called with one/few different arguments** | ||
- a selector is **imported by different modules** at the same time | ||
- **sharing selectors** with props across multiple components (see [reselect example](https://github.com/reactjs/reselect#sharing-selectors-with-props-across-multiple-components) and [re-reselect solution](https://github.com/toomuchdesign/re-reselect#how-to-share-a-selector-across-multiple-components-while-passing-in-props-and-retaining-memoization)) | ||
- selectors need to be **instantiated on runtime** | ||
[reselect]: https://github.com/reactjs/reselect | ||
[ci-img]: https://travis-ci.org/toomuchdesign/re-reselect.svg | ||
[ci]: https://travis-ci.org/toomuchdesign/re-reselect | ||
[reselect]: https://github.com/reactjs/reselect | ||
[ci-img]: https://travis-ci.org/toomuchdesign/re-reselect.svg | ||
[ci]: https://travis-ci.org/toomuchdesign/re-reselect | ||
@@ -22,3 +24,3 @@ ```js | ||
const cachedSelector = createCachedSelector( | ||
// Set up your Reselect selector as normal | ||
// Set up your Reselect selector as normal: | ||
@@ -31,22 +33,60 @@ // reselect inputSelectors: | ||
// reselect resultFunc: | ||
(A, B, someArg) => expensiveComputation(A, B, someArg}), | ||
(A, B, someArg) => expensiveComputation(A, B, someArg), | ||
)( | ||
// Resolver function used as map cache key | ||
// (It takes the same selector arguments and must return a string) | ||
// In this case it will use the second arguments as cache key | ||
/* | ||
* Now it comes the re-reselect caching part: | ||
* declare a resolver function, used as mapping cache key. | ||
* It takes the same arguments as the generated selector | ||
* and must return a string or number (the cache key). | ||
* | ||
* A new selector will be cached for each different returned key | ||
* | ||
* In this example the second argument of the selector is used as cache key | ||
*/ | ||
(state, someArg) => someArg, | ||
); | ||
// 2 different selector are created and cached | ||
const fooSelector = cachedSelector(state, 'foo'); | ||
const barSelector = cachedSelector(state, 'bar'); | ||
// Now you are ready to call/expose the cached selector like a normal selector: | ||
// "foo" hits the cache: "foo" selector is retrieved and called again | ||
const fooAgain = cachedSelector(state, 'foo'); | ||
/* | ||
* Call selector with "foo" and with "bar": | ||
* 2 different selectors are created, called and cached behind the scenes. | ||
* The selectors return their computed result. | ||
*/ | ||
const fooResult = cachedSelector(state, 'foo'); | ||
const barResult = cachedSelector(state, 'bar'); | ||
/* | ||
* Call selector with "foo" again: | ||
* "foo" hits the cache, now: the selector cached under "foo" key | ||
* is retrieved, called again and the result is returned. | ||
*/ | ||
const fooResultAgain = cachedSelector(state, 'foo'); | ||
/* | ||
* Note that fooResult === fooResultAgain. | ||
* The cache was not invalidated by "cachedSelector(state, 'bar')" call | ||
*/ | ||
``` | ||
Jump straight to the [API's](#api). | ||
## Table of contents | ||
- [Installation](#installation) | ||
- [Why? + example](#why--example) | ||
- [Re-reselect solution](#re-reselect-solution) | ||
- [Other viable solutions](#other-viable-solutions) | ||
- [FAQ](#faq) | ||
- [How do I wrap my existing selector with re-reselect?](#how-do-i-wrap-my-existing-selector-with-re-reselect) | ||
- [How to share a selector across multiple components while passing in props and retaining memoization?](#how-to-share-a-selector-across-multiple-components-while-passing-in-props-and-retaining-memoization) | ||
- [How do I test a re-reselect selector?](#how-do-i-test-a-re-reselect-selector) | ||
- [API](#api) | ||
- [`reReselect`](#rereselectreselects-createselector-argumentsresolverfunction-selectorcreator--selectorcreator) | ||
- [reReselectInstance`()`](#rereselectinstanceselectorarguments) | ||
- [reReselectInstance`.getMatchingSelector`](#rereselectinstancegetmatchingselectorselectorarguments) | ||
- [reReselectInstance`.removeMatchingSelector`](#rereselectinstanceremovematchingselectorselectorarguments) | ||
- [reReselectInstance`.clearCache`](#rereselectinstanceclearcache) | ||
- [reReselectInstance`.resultFunc`](#rereselectinstanceresultfunc) | ||
## Installation | ||
```console | ||
npm install reselect | ||
npm install re-reselect | ||
@@ -58,3 +98,3 @@ ``` | ||
On each store update, I had to call the selector again again in order to retrieve all the pieces of data needed by my UI. Like this: | ||
On each store update, I had to repeatedly call the selector in order to retrieve all the pieces of data needed by my UI. Like this: | ||
@@ -71,7 +111,7 @@ ```js | ||
`key` is the string output of the `resolver` function declared in selector initialization phase. | ||
`key` is the output of the `resolver` function, declared at selector initialization. | ||
`resolver` is a custom function which receives the same arguments of final selector (in the example: `state`, `itemId`, `'dataX'`, `otherArgs`) and returns a `string` or `number`. | ||
`resolver` is a custom function which receives the same arguments as the final selector (in the example: `state`, `itemId`, `'dataX'`, `otherArgs`) and returns a `string` or `number`. | ||
That said, I was able to configure `re-reselect` to create a map of selector using the 3rd argument as key: | ||
That said, I was able to configure `re-reselect` to retrieve my data by querying a set of cached selectors using the 3rd argument as cache key: | ||
@@ -91,9 +131,9 @@ ```js | ||
But now, each time a selector is called, the following happens: | ||
But now, **each time the selector is called**, the following happens behind the scenes: | ||
- Run `resolver` function and get its result (the cache key) | ||
- Look for a matching key from the cache | ||
- Return a cached selector or create a new one if no matching key is found in cache | ||
- Call selector with with provided arguments | ||
- Call selector with provided arguments | ||
**Re-reselect** stays completely optional and uses **your installed reselect** library under the hoods (reselect is declared as a **peer depenency**). | ||
**Re-reselect** stays completely optional and uses **your installed reselect** library under the hoods (reselect is declared as a **peer dependency**). | ||
@@ -115,9 +155,8 @@ Furthermore you can use any custom selector (see [API](#api)). | ||
This is what **re-reselect** actually does. It's quite verbose (since should be repeated for each selector), that's why I decided to move it into a separate module. | ||
This is what **re-reselect** actually does. It's quite verbose (since should be repeated for each selector), that's why re-reselect is here. | ||
## FAQ | ||
### Q: How do I wrap my existing selector with re-reselect? | ||
### How do I wrap my existing selector with re-reselect? | ||
Given your `reselect` selectors: | ||
A: Given your `reselect` selectors: | ||
```js | ||
@@ -147,5 +186,66 @@ import { createSelector } from 'reselect'; | ||
``` | ||
Voilà! | ||
Voilà, `getMyData` is ready for use! | ||
```js | ||
let myData = getMyData(state, 'foo', 'bar'); | ||
### Q: How do I test a re-reselect selector? | ||
``` | ||
### How to share a selector across multiple components while passing in props and retaining memoization? | ||
This example is how `re-reselect` would solve the scenario described in [Reselect docs](https://github.com/reactjs/reselect#sharing-selectors-with-props-across-multiple-components). | ||
We can directly declare `getVisibleTodos` selector. Since `re-reselect` handles selectors instantiation transparently, there is no need to declare a `makeGetVisibleTodos` factory. | ||
#### `selectors/todoSelectors.js` | ||
```js | ||
import createCachedSelector from 're-reselect'; | ||
const getVisibilityFilter = (state, props) => | ||
state.todoLists[props.listId].visibilityFilter | ||
const getTodos = (state, props) => | ||
state.todoLists[props.listId].todos | ||
const getVisibleTodos = createCachedSelector( | ||
[ getVisibilityFilter, getTodos ], | ||
(visibilityFilter, todos) => { | ||
switch (visibilityFilter) { | ||
case 'SHOW_COMPLETED': | ||
return todos.filter(todo => todo.completed) | ||
case 'SHOW_ACTIVE': | ||
return todos.filter(todo => !todo.completed) | ||
default: | ||
return todos | ||
} | ||
} | ||
)( | ||
/* | ||
* Re-reselect resolver function. | ||
* Cache/call a new selector for each different "listId" | ||
*/ | ||
(state, props) => props.listId, | ||
); | ||
export default getVisibleTodos; | ||
``` | ||
#### `containers/VisibleTodoList.js` | ||
```js | ||
import { connect } from 'react-redux' | ||
import { toggleTodo } from '../actions' | ||
import TodoList from '../components/TodoList' | ||
import { getVisibleTodos } from '../selectors' | ||
// No need of makeMapStateToProps function: | ||
// use getVisibleTodos as a normal selector | ||
const mapStateToProps = (state, props) => { | ||
return { | ||
todos: getVisibleTodos(state, props) | ||
} | ||
} | ||
// ... | ||
``` | ||
### How do I test a re-reselect selector? | ||
Just like a normal reselect selector! Read more [here](https://github.com/reactjs/reselect#q-how-do-i-test-a-selector). | ||
@@ -213,3 +313,3 @@ | ||
#### Returns | ||
(Function): a `reReselectInstance` ready to be called to retrieve data from your store. | ||
(Function): a `reReselectInstance` selector ready to be used **like a normal reselect selector**. | ||
@@ -219,2 +319,4 @@ ### reReselectInstance(selectorArguments) | ||
>The followings are advanced methods and you won't need them for basic usage! | ||
### reReselectInstance`.getMatchingSelector(selectorArguments)` | ||
@@ -229,3 +331,7 @@ Retrieve the selector responding to the given arguments. | ||
### reReselectInstance`.resultFunc` | ||
Get `resultFunc` for easily [test composed selectors](https://github.com/reactjs/reselect#q-how-do-i-test-a-selector). | ||
## Todo's | ||
- Named exports? | ||
- TypeScript/Flux type definitions? |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
20156
130
328