Skip to content

Commit 6304f01

Browse files
committed
Remove deps of useSelector
1 parent 06a674e commit 6304f01

File tree

3 files changed

+68
-17
lines changed

3 files changed

+68
-17
lines changed

docs/api/hooks.md

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ From there, you may import any of the listed React Redux hooks APIs and use them
3333
## `useSelector()`
3434

3535
```js
36-
const result : any = useSelector(selector : Function, deps : any[])
36+
const result : any = useSelector(selector : Function)
3737
```
3838

3939
Allows you to extract data from the Redux store state, using a selector function.
@@ -43,7 +43,6 @@ The selector is approximately equivalent to the [`mapStateToProps` argument to `
4343
However, there are some differences between the selectors passed to `useSelector()` and a `mapState` function:
4444

4545
- The selector may return any value as a result, not just an object. The return value of the selector will be used as the return value of the `useSelector()` hook.
46-
- The selector function used will be based on the `deps` array. If no deps array is provided, the latest passed-in selector function will be used when the component renders, and also when any actions are dispatched before the next render. If a deps array is provided, the last saved selector will be used, and that selector will be overwritten whenever the deps array contents have changed.
4746
- When an action is dispatched, `useSelector()` will do a shallow comparison of the previous selector result value and the current result value. If they are different, the component will be forced to re-render. If they are the same, they component will not re-render.
4847
- The selector function does _not_ receive an `ownProps` argument. If you wish to use props within the selector function to determine what values to extract, you should call the React [`useMemo()`](https://reactjs.org/docs/hooks-reference.html#usememo) or [`useCallback()`](https://reactjs.org/docs/hooks-reference.html#usecallback) hooks yourself to create a version of the selector that will be re-created whenever the props it depends on change.
4948

@@ -68,16 +67,71 @@ export const CounterComponent = () => {
6867
Using props to determine what to extract:
6968

7069
```jsx
71-
import React from 'react'
70+
import React, {useCallback} from 'react'
7271
import { useSelector } from 'react-redux'
7372

74-
export const TodoListItem = props => (
75-
const todo = useSelector(state => state.todos[props.id], [props.id])
73+
export const TodoListItem = ({id}) => (
74+
const selector = useCallback(state => state.todos[id], [id])
75+
const todo = useSelector(selector)
7676

7777
return <div>{todo.text}</div>
7878
}
7979
```
8080
81+
With Reselect:
82+
83+
```jsx
84+
// ducks/todos.js
85+
import { createSelector } from 'reselect'
86+
87+
export const selectPendingTodos = createSelector(
88+
(state) => state.todos,
89+
(todos) => todos.filter(todo => todo.completed !== true)
90+
)
91+
92+
export const selectNPendingTodos = createSelector(
93+
selectPendingTodos,
94+
pendingTodos => pendingTodos.length
95+
)
96+
97+
// component.js
98+
99+
import React from 'react'
100+
import { useSelector } from 'react-redux'
101+
import { selectNPendingTodos } from 'ducks/todos'
102+
103+
export constPendingTodos = () => {
104+
const nPendingTodos = useSelector(selectNPendingTodos)
105+
return <div>{nPendingTodos}</div>
106+
}
107+
```
108+
109+
With Reselect and factory-selectors:
110+
111+
```jsx
112+
// ducks/todos.js
113+
import { createSelector } from 'reselect'
114+
115+
export const makeSelectTodoById = (id) => createSelector(
116+
(state) => state.todos,
117+
(todos) => todos.filter(todo => todo.id === id)
118+
)
119+
120+
121+
// component.js
122+
123+
import React from 'react'
124+
import { useSelector } from 'react-redux'
125+
import { makeSelectTodoById } from 'ducks/todos'
126+
127+
export const Todo = ({id}) => {
128+
const todoSelector = useMemo(() => makeSelectTodoById(id), [id])
129+
const todo = useSelector(todoSelector)
130+
131+
return <div>{todo.name}</div>
132+
}
133+
```
134+
81135
## `useActions()`
82136
83137
```js

src/hooks/useSelector.js

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ const useIsomorphicLayoutEffect =
2424
* useful if you provide a selector that memoizes values).
2525
*
2626
* @param {Function} selector the selector function
27-
* @param {any[]} deps (optional) dependencies array to control referential stability
28-
* of the selector
2927
*
3028
* @returns {any} the selected state
3129
*
@@ -36,11 +34,11 @@ const useIsomorphicLayoutEffect =
3634
* import { RootState } from './store'
3735
*
3836
* export const CounterComponent = () => {
39-
* const counter = useSelector(state => state.counter, [])
37+
* const counter = useSelector(state => state.counter)
4038
* return <div>{counter}</div>
4139
* }
4240
*/
43-
export function useSelector(selector, deps) {
41+
export function useSelector(selector) {
4442
invariant(selector, `You must pass a selector to useSelectors`)
4543

4644
const { store, subscription: contextSub } = useReduxContext()
@@ -51,15 +49,13 @@ export function useSelector(selector, deps) {
5149
contextSub
5250
])
5351

54-
const memoizedSelector = useMemo(() => selector, deps)
55-
5652
const latestSubscriptionCallbackError = useRef()
57-
const latestSelector = useRef(memoizedSelector)
53+
const latestSelector = useRef(selector)
5854

5955
let selectedState = undefined
6056

6157
try {
62-
selectedState = memoizedSelector(store.getState())
58+
selectedState = selector(store.getState())
6359
} catch (err) {
6460
let errorMessage = `An error occured while selecting the store state: ${
6561
err.message
@@ -77,7 +73,7 @@ export function useSelector(selector, deps) {
7773
const latestSelectedState = useRef(selectedState)
7874

7975
useIsomorphicLayoutEffect(() => {
80-
latestSelector.current = memoizedSelector
76+
latestSelector.current = selector
8177
latestSelectedState.current = selectedState
8278
latestSubscriptionCallbackError.current = undefined
8379
})

test/hooks/useSelector.spec.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*eslint-disable react/prop-types*/
22

3-
import React, { useReducer } from 'react'
3+
import React, { useReducer, useCallback } from 'react'
44
import { createStore } from 'redux'
55
import { renderHook, act } from 'react-hooks-testing-library'
66
import * as rtl from 'react-testing-library'
@@ -155,15 +155,16 @@ describe('React', () => {
155155
expect(renderedItems.length).toBe(1)
156156
})
157157

158-
it('re-uses the selector if deps do not change', () => {
158+
it('re-uses the selector if it does not change', () => {
159159
let selectorId = 0
160160
let forceRender
161161

162162
const Comp = () => {
163163
const [, f] = useReducer(c => c + 1, 0)
164164
forceRender = f
165165
const renderedSelectorId = selectorId++
166-
const value = useSelector(() => renderedSelectorId, [])
166+
const selector = useCallback(() => renderedSelectorId, [])
167+
const value = useSelector(selector)
167168
renderedItems.push(value)
168169
return <div />
169170
}

0 commit comments

Comments
 (0)