[d24f17c] | 1 | # Reselect
|
---|
| 2 |
|
---|
| 3 | [![npm package][npm-badge]][npm][![Coveralls][coveralls-badge]][coveralls][![GitHub Workflow Status][build-badge]][build]![TypeScript][typescript-badge]
|
---|
| 4 |
|
---|
| 5 | A library for creating memoized "selector" functions. Commonly used with Redux, but usable with any plain JS immutable data as well.
|
---|
| 6 |
|
---|
| 7 | - Selectors can compute derived data, allowing [Redux] to store the minimal possible state.
|
---|
| 8 | - Selectors are efficient. A selector is not recomputed unless one of its arguments changes.
|
---|
| 9 | - Selectors are composable. They can be used as input to other selectors.
|
---|
| 10 |
|
---|
| 11 | The **Redux docs usage page on [Deriving Data with Selectors](https://redux.js.org/usage/deriving-data-selectors)** covers the purpose and motivation for selectors, why memoized selectors are useful, typical Reselect usage patterns, and using selectors with [React-Redux].
|
---|
| 12 |
|
---|
| 13 | ## Installation
|
---|
| 14 |
|
---|
| 15 | ### Redux Toolkit
|
---|
| 16 |
|
---|
| 17 | While Reselect is not exclusive to [Redux], it is already included by default in [the official Redux Toolkit package](https://redux-toolkit.js.org) - no further installation needed.
|
---|
| 18 |
|
---|
| 19 | ```ts
|
---|
| 20 | import { createSelector } from '@reduxjs/toolkit'
|
---|
| 21 | ```
|
---|
| 22 |
|
---|
| 23 | ### Standalone
|
---|
| 24 |
|
---|
| 25 | For standalone usage, install the `reselect` package:
|
---|
| 26 |
|
---|
| 27 | ```bash
|
---|
| 28 | # NPM
|
---|
| 29 | npm install reselect
|
---|
| 30 |
|
---|
| 31 | # Yarn
|
---|
| 32 | yarn add reselect
|
---|
| 33 | ```
|
---|
| 34 |
|
---|
| 35 | ---
|
---|
| 36 |
|
---|
| 37 | ## Documentation
|
---|
| 38 |
|
---|
| 39 | The Reselect docs are available at **https://reselect.js.org**, and include usage guides and API references:
|
---|
| 40 |
|
---|
| 41 | - [**Introduction**](#https://reselect.js.org/introduction/getting-started)
|
---|
| 42 | - [**How Does Reselect Work?**](#https://reselect.js.org/introduction/how-does-reselect-work)
|
---|
| 43 | - **API Reference**:
|
---|
| 44 | - **[`createSelector`]**
|
---|
| 45 | - **[`createSelectorCreator`]**
|
---|
| 46 | - **[`createStructuredSelector`]**
|
---|
| 47 | - [**Development-Only Stability Checks**](#https://reselect.js.org/api/development-only-stability-checks)
|
---|
| 48 | - **[`lruMemoize`]**
|
---|
| 49 | - **[`weakMapMemoize`]**
|
---|
| 50 | - [**FAQ**](#https://reselect.js.org/FAQ)
|
---|
| 51 |
|
---|
| 52 | ## Basic Usage
|
---|
| 53 |
|
---|
| 54 | Reselect exports a [`createSelector`] API, which generates memoized selector functions. [`createSelector`] accepts one or more [input selectors], which extract values from arguments, and a [result function] function that receives the extracted values and should return a derived value. If the generated [output selector] is called multiple times, the output will only be recalculated when the extracted values have changed.
|
---|
| 55 |
|
---|
| 56 | You can play around with the following **example** in [this CodeSandbox](https://codesandbox.io/s/reselect-example-g3k9gf?file=/src/index.js):
|
---|
| 57 |
|
---|
| 58 | ```ts
|
---|
| 59 | import { createSelector } from 'reselect'
|
---|
| 60 |
|
---|
| 61 | interface RootState {
|
---|
| 62 | todos: { id: number; completed: boolean }[]
|
---|
| 63 | alerts: { id: number; read: boolean }[]
|
---|
| 64 | }
|
---|
| 65 |
|
---|
| 66 | const state: RootState = {
|
---|
| 67 | todos: [
|
---|
| 68 | { id: 0, completed: false },
|
---|
| 69 | { id: 1, completed: true }
|
---|
| 70 | ],
|
---|
| 71 | alerts: [
|
---|
| 72 | { id: 0, read: false },
|
---|
| 73 | { id: 1, read: true }
|
---|
| 74 | ]
|
---|
| 75 | }
|
---|
| 76 |
|
---|
| 77 | const selectCompletedTodos = (state: RootState) => {
|
---|
| 78 | console.log('selector ran')
|
---|
| 79 | return state.todos.filter(todo => todo.completed === true)
|
---|
| 80 | }
|
---|
| 81 |
|
---|
| 82 | selectCompletedTodos(state) // selector ran
|
---|
| 83 | selectCompletedTodos(state) // selector ran
|
---|
| 84 | selectCompletedTodos(state) // selector ran
|
---|
| 85 |
|
---|
| 86 | const memoizedSelectCompletedTodos = createSelector(
|
---|
| 87 | [(state: RootState) => state.todos],
|
---|
| 88 | todos => {
|
---|
| 89 | console.log('memoized selector ran')
|
---|
| 90 | return todos.filter(todo => todo.completed === true)
|
---|
| 91 | }
|
---|
| 92 | )
|
---|
| 93 |
|
---|
| 94 | memoizedSelectCompletedTodos(state) // memoized selector ran
|
---|
| 95 | memoizedSelectCompletedTodos(state)
|
---|
| 96 | memoizedSelectCompletedTodos(state)
|
---|
| 97 |
|
---|
| 98 | console.log(selectCompletedTodos(state) === selectCompletedTodos(state)) //=> false
|
---|
| 99 |
|
---|
| 100 | console.log(
|
---|
| 101 | memoizedSelectCompletedTodos(state) === memoizedSelectCompletedTodos(state)
|
---|
| 102 | ) //=> true
|
---|
| 103 | ```
|
---|
| 104 |
|
---|
| 105 | As you can see from the example above, `memoizedSelectCompletedTodos` does not run the second or third time, but we still get the same return value as last time.
|
---|
| 106 |
|
---|
| 107 | In addition to skipping unnecessary recalculations, `memoizedSelectCompletedTodos` returns the existing result reference if there is no recalculation. This is important for libraries like [React-Redux] or [React] that often rely on reference equality checks to optimize UI updates.
|
---|
| 108 |
|
---|
| 109 | ---
|
---|
| 110 |
|
---|
| 111 | ## Terminology
|
---|
| 112 |
|
---|
| 113 | - <a name="selector-function"></a>[**Selector Function**](#selector-function): A function that accepts one or more JavaScript values as arguments, and derives a result. When used with [Redux], the first argument is typically the entire Redux store state.
|
---|
| 114 | - <a name="input-selectors"></a>[**input selectors**](#input-selectors): Basic selector functions used as building blocks for creating a memoized selector. They are passed as the first argument(s) to [`createSelector`], and are called with all selector arguments. They are responsible for extracting and providing necessary values to the [result function].
|
---|
| 115 | - <a name="output-selector"></a>[**Output Selector**](#output-selector): The actual memoized selectors created by [`createSelector`].
|
---|
| 116 | - <a name="result-function"></a>[**Result Function**](#result-function): The function that comes after the [input selectors]. It takes the [input selectors]' return values as arguments and returns a result.
|
---|
| 117 | - <a name="dependencies"></a>[**`Dependencies`**](#dependencies): Same as [input selectors]. They are what the [output selector] "depends" on.
|
---|
| 118 |
|
---|
| 119 | The below example serves as a visual aid:
|
---|
| 120 |
|
---|
| 121 | ```ts
|
---|
| 122 | const outputSelector = createSelector(
|
---|
| 123 | [inputSelector1, inputSelector2, inputSelector3], // synonymous with `dependencies`.
|
---|
| 124 | resultFunc // Result function
|
---|
| 125 | )
|
---|
| 126 | ```
|
---|
| 127 |
|
---|
| 128 | ---
|
---|
| 129 |
|
---|
| 130 | ## What's New in 5.0.0?
|
---|
| 131 |
|
---|
| 132 | Version 5.0.0 introduces several new features and improvements:
|
---|
| 133 |
|
---|
| 134 | - **Customization Enhancements**:
|
---|
| 135 |
|
---|
| 136 | - Added the ability to pass an options object to [`createSelectorCreator`], allowing for customized `memoize` and `argsMemoize` functions, alongside their respective options (`memoizeOptions` and `argsMemoizeOptions`).
|
---|
| 137 | - The [`createSelector`] function now supports direct customization of `memoize` and `argsMemoize` within its options object.
|
---|
| 138 |
|
---|
| 139 | - **Memoization Functions**:
|
---|
| 140 |
|
---|
| 141 | - Introduced new experimental memoization functions: `weakMapMemoize` and `unstable_autotrackMemoize`.
|
---|
| 142 | - Incorporated `memoize` and `argsMemoize` into the [output selector fields] for debugging purposes.
|
---|
| 143 |
|
---|
| 144 | - **TypeScript Support and Performance**:
|
---|
| 145 |
|
---|
| 146 | - Discontinued support for TypeScript versions below 4.7, aligning with modern TypeScript features.
|
---|
| 147 | - Significantly improved TypeScript performance for nesting [output selectors][output selector]. The nesting limit has increased from approximately 8 to around 30 [output selectors][output selector], greatly reducing the occurrence of the infamous `Type instantiation is excessively deep and possibly infinite` error.
|
---|
| 148 |
|
---|
| 149 | - **Selector API Enhancements**:
|
---|
| 150 |
|
---|
| 151 | - Removed the second overload of `createStructuredSelector` due to its susceptibility to runtime errors.
|
---|
| 152 |
|
---|
| 153 | - **Additional Functionalities**:
|
---|
| 154 |
|
---|
| 155 | - Added `dependencyRecomputations` and `resetDependencyRecomputations` to the [output selector fields]. These additions provide greater control and insight over [input selectors], complementing the new `argsMemoize` API.
|
---|
| 156 | - Introduced `inputStabilityCheck`, a development tool that runs the [input selectors] twice using the same arguments and triggers a warning If they return differing results for the same call.
|
---|
| 157 | - Introduced `identityFunctionCheck`, a development tool that checks to see if the [result function] returns its own input.
|
---|
| 158 |
|
---|
| 159 | These updates aim to enhance flexibility, performance, and developer experience. For detailed usage and examples, refer to the updated documentation sections for each feature.
|
---|
| 160 |
|
---|
| 161 | - **Breaking Changes**:
|
---|
| 162 |
|
---|
| 163 | - Removed `ParametricSelector` and `OutputParametricSelector` types. Their functionalities are now integrated into `Selector` and `OutputSelector` respectively, which inherently support additional parameters.
|
---|
| 164 |
|
---|
| 165 | <div align="right">[ <a href="installation">↑ Back to top ↑</a> ]</div>
|
---|
| 166 |
|
---|
| 167 | ---
|
---|
| 168 |
|
---|
| 169 | ## License
|
---|
| 170 |
|
---|
| 171 | MIT
|
---|
| 172 |
|
---|
| 173 | ## References
|
---|
| 174 |
|
---|
| 175 | <details><summary><b>Click to Expand</b></summary>
|
---|
| 176 |
|
---|
| 177 | Originally inspired by getters in [NuclearJS](https://github.com/optimizely/nuclear-js.git), [subscriptions](https://github.com/Day8/re-frame#just-a-read-only-cursor) in [re-frame](https://github.com/Day8/re-frame) and this [proposal](https://github.com/reduxjs/redux/pull/169) from [speedskater](https://github.com/speedskater).
|
---|
| 178 |
|
---|
| 179 | [typescript-badge]: https://img.shields.io/badge/TypeScript-v4%2E7%2B-007ACC?style=for-the-badge&logo=TypeScript&logoColor=black&labelColor=blue&color=gray
|
---|
| 180 | [build-badge]: https://img.shields.io/github/actions/workflow/status/reduxjs/reselect/build-and-test-types.yml?branch=master&style=for-the-badge
|
---|
| 181 | [build]: https://github.com/reduxjs/reselect/actions/workflows/build-and-test-types.yml
|
---|
| 182 | [npm-badge]: https://img.shields.io/npm/v/reselect.svg?style=for-the-badge
|
---|
| 183 | [npm]: https://www.npmjs.org/package/reselect
|
---|
| 184 | [coveralls-badge]: https://img.shields.io/coveralls/reduxjs/reselect/master.svg?style=for-the-badge
|
---|
| 185 | [coveralls]: https://coveralls.io/github/reduxjs/reselect
|
---|
| 186 |
|
---|
| 187 | <!-- External Links -->
|
---|
| 188 |
|
---|
| 189 | [Redux]: https://redux.js.org 'Redux'
|
---|
| 190 | [React]: https://react.dev 'React'
|
---|
| 191 | [React-Redux]: https://react-redux.js.org 'React-Redux'
|
---|
| 192 |
|
---|
| 193 | <!-- Internal Links -->
|
---|
| 194 |
|
---|
| 195 | [selector]: #selector-function 'Selector Function'
|
---|
| 196 | [input selectors]: #input-selectors 'Input Selectors'
|
---|
| 197 | [output selector]: #output-selector 'Output Selector'
|
---|
| 198 | [result function]: #result-function 'Result Function'
|
---|
| 199 | [output selector fields]: https://reselect.js.org/api/createSelector#output-selector-fields 'Output Selector Fields'
|
---|
| 200 | [`createSelector`]: https://reselect.js.org/api/createSelector 'createSelector'
|
---|
| 201 | [`createSelectorCreator`]: https://reselect.js.org/api/createSelectorCreator 'createSelectorCreator'
|
---|
| 202 | [`lruMemoize`]: https://reselect.js.org/api/lruMemoize 'lruMemoize'
|
---|
| 203 | [`weakMapMemoize`]: https://reselect.js.org/api/weakMapMemoize 'weakMapMemoize'
|
---|
| 204 | [`createStructuredSelector`]: https://reselect.js.org/api/createStructuredSelector 'createStructuredSelector'
|
---|
| 205 |
|
---|
| 206 | </details>
|
---|