source: node_modules/react-redux/src/hooks/useSelector.ts@ a762898

Last change on this file since a762898 was a762898, checked in by istevanoska <ilinastevanoska@…>, 5 months ago

Added visualizations

  • Property mode set to 100644
File size: 9.5 KB
Line 
1//import * as React from 'react'
2import { React } from '../utils/react'
3import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector.js'
4import type { ReactReduxContextValue } from '../components/Context'
5import { ReactReduxContext } from '../components/Context'
6import type { EqualityFn, NoInfer } from '../types'
7import {
8 createReduxContextHook,
9 useReduxContext as useDefaultReduxContext,
10} from './useReduxContext'
11
12/**
13 * The frequency of development mode checks.
14 *
15 * @since 8.1.0
16 * @internal
17 */
18export type DevModeCheckFrequency = 'never' | 'once' | 'always'
19
20/**
21 * Represents the configuration for development mode checks.
22 *
23 * @since 9.0.0
24 * @internal
25 */
26export interface DevModeChecks {
27 /**
28 * Overrides the global stability check for the selector.
29 * - `once` - Run only the first time the selector is called.
30 * - `always` - Run every time the selector is called.
31 * - `never` - Never run the stability check.
32 *
33 * @default 'once'
34 *
35 * @since 8.1.0
36 */
37 stabilityCheck: DevModeCheckFrequency
38
39 /**
40 * Overrides the global identity function check for the selector.
41 * - `once` - Run only the first time the selector is called.
42 * - `always` - Run every time the selector is called.
43 * - `never` - Never run the identity function check.
44 *
45 * **Note**: Previously referred to as `noopCheck`.
46 *
47 * @default 'once'
48 *
49 * @since 9.0.0
50 */
51 identityFunctionCheck: DevModeCheckFrequency
52}
53
54export interface UseSelectorOptions<Selected = unknown> {
55 equalityFn?: EqualityFn<Selected>
56
57 /**
58 * `useSelector` performs additional checks in development mode to help
59 * identify and warn about potential issues in selector behavior. This
60 * option allows you to customize the behavior of these checks per selector.
61 *
62 * @since 9.0.0
63 */
64 devModeChecks?: Partial<DevModeChecks>
65}
66
67/**
68 * Represents a custom hook that allows you to extract data from the
69 * Redux store state, using a selector function. The selector function
70 * takes the current state as an argument and returns a part of the state
71 * or some derived data. The hook also supports an optional equality
72 * function or options object to customize its behavior.
73 *
74 * @template StateType - The specific type of state this hook operates on.
75 *
76 * @public
77 */
78export interface UseSelector<StateType = unknown> {
79 /**
80 * A function that takes a selector function as its first argument.
81 * The selector function is responsible for selecting a part of
82 * the Redux store's state or computing derived data.
83 *
84 * @param selector - A function that receives the current state and returns a part of the state or some derived data.
85 * @param equalityFnOrOptions - An optional equality function or options object for customizing the behavior of the selector.
86 * @returns The selected part of the state or derived data.
87 *
88 * @template TState - The specific type of state this hook operates on.
89 * @template Selected - The type of the value that the selector function will return.
90 */
91 <TState extends StateType = StateType, Selected = unknown>(
92 selector: (state: TState) => Selected,
93 equalityFnOrOptions?: EqualityFn<Selected> | UseSelectorOptions<Selected>,
94 ): Selected
95
96 /**
97 * Creates a "pre-typed" version of {@linkcode useSelector useSelector}
98 * where the `state` type is predefined.
99 *
100 * This allows you to set the `state` type once, eliminating the need to
101 * specify it with every {@linkcode useSelector useSelector} call.
102 *
103 * @returns A pre-typed `useSelector` with the state type already defined.
104 *
105 * @example
106 * ```ts
107 * export const useAppSelector = useSelector.withTypes<RootState>()
108 * ```
109 *
110 * @template OverrideStateType - The specific type of state this hook operates on.
111 *
112 * @since 9.1.0
113 */
114 withTypes: <
115 OverrideStateType extends StateType,
116 >() => UseSelector<OverrideStateType>
117}
118
119const refEquality: EqualityFn<any> = (a, b) => a === b
120
121/**
122 * Hook factory, which creates a `useSelector` hook bound to a given context.
123 *
124 * @param {React.Context} [context=ReactReduxContext] Context passed to your `<Provider>`.
125 * @returns {Function} A `useSelector` hook bound to the specified context.
126 */
127export function createSelectorHook(
128 context: React.Context<ReactReduxContextValue<
129 any,
130 any
131 > | null> = ReactReduxContext,
132): UseSelector {
133 const useReduxContext =
134 context === ReactReduxContext
135 ? useDefaultReduxContext
136 : createReduxContextHook(context)
137
138 const useSelector = <TState, Selected>(
139 selector: (state: TState) => Selected,
140 equalityFnOrOptions:
141 | EqualityFn<NoInfer<Selected>>
142 | UseSelectorOptions<NoInfer<Selected>> = {},
143 ): Selected => {
144 const { equalityFn = refEquality } =
145 typeof equalityFnOrOptions === 'function'
146 ? { equalityFn: equalityFnOrOptions }
147 : equalityFnOrOptions
148 if (process.env.NODE_ENV !== 'production') {
149 if (!selector) {
150 throw new Error(`You must pass a selector to useSelector`)
151 }
152 if (typeof selector !== 'function') {
153 throw new Error(`You must pass a function as a selector to useSelector`)
154 }
155 if (typeof equalityFn !== 'function') {
156 throw new Error(
157 `You must pass a function as an equality function to useSelector`,
158 )
159 }
160 }
161
162 const reduxContext = useReduxContext()
163
164 const { store, subscription, getServerState } = reduxContext
165
166 const firstRun = React.useRef(true)
167
168 const wrappedSelector = React.useCallback<typeof selector>(
169 {
170 [selector.name](state: TState) {
171 const selected = selector(state)
172 if (process.env.NODE_ENV !== 'production') {
173 const { devModeChecks = {} } =
174 typeof equalityFnOrOptions === 'function'
175 ? {}
176 : equalityFnOrOptions
177 const { identityFunctionCheck, stabilityCheck } = reduxContext
178 const {
179 identityFunctionCheck: finalIdentityFunctionCheck,
180 stabilityCheck: finalStabilityCheck,
181 } = {
182 stabilityCheck,
183 identityFunctionCheck,
184 ...devModeChecks,
185 }
186 if (
187 finalStabilityCheck === 'always' ||
188 (finalStabilityCheck === 'once' && firstRun.current)
189 ) {
190 const toCompare = selector(state)
191 if (!equalityFn(selected, toCompare)) {
192 let stack: string | undefined = undefined
193 try {
194 throw new Error()
195 } catch (e) {
196 // eslint-disable-next-line no-extra-semi
197 ;({ stack } = e as Error)
198 }
199 console.warn(
200 'Selector ' +
201 (selector.name || 'unknown') +
202 ' returned a different result when called with the same parameters. This can lead to unnecessary rerenders.' +
203 '\nSelectors that return a new reference (such as an object or an array) should be memoized: https://redux.js.org/usage/deriving-data-selectors#optimizing-selectors-with-memoization',
204 {
205 state,
206 selected,
207 selected2: toCompare,
208 stack,
209 },
210 )
211 }
212 }
213 if (
214 finalIdentityFunctionCheck === 'always' ||
215 (finalIdentityFunctionCheck === 'once' && firstRun.current)
216 ) {
217 // @ts-ignore
218 if (selected === state) {
219 let stack: string | undefined = undefined
220 try {
221 throw new Error()
222 } catch (e) {
223 // eslint-disable-next-line no-extra-semi
224 ;({ stack } = e as Error)
225 }
226 console.warn(
227 'Selector ' +
228 (selector.name || 'unknown') +
229 ' returned the root state when called. This can lead to unnecessary rerenders.' +
230 '\nSelectors that return the entire state are almost certainly a mistake, as they will cause a rerender whenever *anything* in state changes.',
231 { stack },
232 )
233 }
234 }
235 if (firstRun.current) firstRun.current = false
236 }
237 return selected
238 },
239 }[selector.name],
240 [selector],
241 )
242
243 const selectedState = useSyncExternalStoreWithSelector(
244 subscription.addNestedSub,
245 store.getState,
246 getServerState || store.getState,
247 wrappedSelector,
248 equalityFn,
249 )
250
251 React.useDebugValue(selectedState)
252
253 return selectedState
254 }
255
256 Object.assign(useSelector, {
257 withTypes: () => useSelector,
258 })
259
260 return useSelector as UseSelector
261}
262
263/**
264 * A hook to access the redux store's state. This hook takes a selector function
265 * as an argument. The selector is called with the store state.
266 *
267 * This hook takes an optional equality comparison function as the second parameter
268 * that allows you to customize the way the selected state is compared to determine
269 * whether the component needs to be re-rendered.
270 *
271 * @param {Function} selector the selector function
272 * @param {Function=} equalityFn the function that will be used to determine equality
273 *
274 * @returns {any} the selected state
275 *
276 * @example
277 *
278 * import React from 'react'
279 * import { useSelector } from 'react-redux'
280 *
281 * export const CounterComponent = () => {
282 * const counter = useSelector(state => state.counter)
283 * return <div>{counter}</div>
284 * }
285 */
286export const useSelector = /*#__PURE__*/ createSelectorHook()
Note: See TracBrowser for help on using the repository browser.