Выпущен React-Redux 7.1.
Краткое введение в API
Потому что в новом проекте используются хуки, но при их использовании все еще используется react-redux.alpha.x
статус версии. Отсутствие возможности использовать новейший API не очень хорошо. К счастью, за последние два дня вышла версия 7.1.
Теперь давайте посмотрим, как использовать этот новый API.
useSelector()
const result : any = useSelector(selector : Function, equalityFn? : Function)
Для чего это? Он предназначен для извлечения данных (состояния) из объекта хранилища избыточности.
Уведомление:Поскольку это может быть выполнено несколько раз в любое время, вы хотите, чтобы селектор оставался чистой функцией.
Этот метод селектора похож на предыдущую концепцию параметра mapStateToProps соединения. а такжеuseSelector
Подпишется на хранилище и запустит селектор при отправке действия.
Конечно, только концепция похожа на mapStateToProps, но различия определенно есть.Взгляните на некоторые различия между селектором и mapStateToProps:
- selector вернет в результате любое значение, а не только объекты. Затем результат, возвращаемый этим селектором, будет использоваться как
useSelector
результат возврата. - Когда действие отправлено,
useSelector()
Производится неглубокое сравнение между предыдущим значением результата селектора и текущим значением результата.Если отличается, он будет перерисован. наоборот. - Селектор не получает параметр ownProps, однако свойства можно использовать с помощью замыканий (пример ниже) или с помощью каррированного селектора.
- Необходимо соблюдать большую осторожность при использовании запоминающих селекторов (примеры ниже).
-
useSelector()
Использовать по умолчанию===
(строгое равенство) проверка на равенство, а не поверхностное равенство (==
).
вы можете вызвать внутри компонентаuseSelector
много раз, но даuseSelector()
Каждый вызов хранилища избыточности создает одну подписку на хранилище избыточности. Из-за поведения пакетного обновления реакции, используемого в версии react-reduxv7, значение, возвращаемое несколькими useSelector в одном и том же компоненте, будет повторно отображаться только один раз.
Равенство сравнить и обновить
Когда функциональный компонент визуализируется, вызывается предоставленная функция селектора, иuseSelector
возвращает свой результат. (Если селектор запущен и не изменился, возвращается кэшированный результат).
Как упоминалось выше, он будет перерендерен только тогда, когда результаты сравнения будут другими. Начиная с версии 7.1.0-alpha.5 сравнение по умолчанию является строгим сравнением (===
). Это отличается от подключения, подключение используетповерхностное сравнение. как использовать эту паруuseSelector()
Есть несколько эффектов.
использоватьmapState
, все отдельные свойства возвращаются в составе составного объекта. Неважно, является ли возвращенный объект новой ссылкой -connect()
Сравнивайте только отдельные поля. использоватьuseSelector
Это не сработает, по умолчанию всегда будет выполняться принудительный повторный рендеринг, если каждый раз возвращается новый объект. Если вы хотите получить несколько значений из хранилища, вы можете сделать это:
-
useSelector()
Вызывается несколько раз, каждый раз возвращая значение поля. -
Используйте Reselect или аналогичную библиотеку для создания мемоизированного селектора, который возвращает несколько значений в объекте, но возвращает новый объект только в случае изменения одного из значений.
-
Использование реакции-редукции
shallowEqual
функционировать какuseSelector
изequalityFn
параметр.
Как следующее:
import { shallowEqual, useSelector } from 'react-redux'
// later
const selectedData = useSelector(selectorReturningObject, shallowEqual)
пример использования селектора
Некоторые основные пояснения были сделаны выше, и некоторые примеры следует использовать для углубления понимания.
Основное использование
import React from 'react'
import { useSelector } from 'react-redux'
export const CounterComponent = () => {
const counter = useSelector(state => state.counter)
return <div>{counter}</div>
}
Используйте свойства через замыкание, чтобы определить, что извлекать:
import React from 'react'
import { useSelector } from 'react-redux'
export const TodoListItem = props => {
const todo = useSelector(state => state.todos[props.id])
return <div>{todo.text}</div>
}
Используйте запоминающие селекторы
Не много знаете о запоминании, вы можетеиди сюдаК пониманию.
При использовании встроенного селектора, как показано вышеuseSelector
Когда компонент визуализируется, создается новый экземпляр селектора. Это работает до тех пор, пока селектор не поддерживает никакого состояния. Однако запоминающие селекторы имеют внутреннее состояние, поэтому при их использовании следует соблюдать осторожность.
Когда селектор зависит только от состояния, просто убедитесь, что он объявлен вне компонента, чтобы каждый рендеринг использовал один и тот же экземпляр селектора:
import React from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect' //上面提到的reselect库
const selectNumOfDoneTodos = createSelector(
state => state.todos,
todos => todos.filter(todo => todo.isDone).length
)
export const DoneTodosCounter = () => {
const NumOfDoneTodos = useSelector(selectNumOfDoneTodos)
return <div>{NumOfDoneTodos}</div>
}
export const App = () => {
return (
<>
<span>Number of done todos:</span>
<DoneTodosCounter />
</>
)
}
То же самое верно, если селектор зависит от свойств компонента, но будет использоваться только в одном экземпляре одного компонента:
import React from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'
const selectNumOfTodosWithIsDoneValue = createSelector(
state => state.todos,
(_, isDone) => isDone,
(todos, isDone) => todos.filter(todo => todo.isDone === isDone).length
)
export const TodoCounterForIsDoneValue = ({ isDone }) => {
const NumOfTodosWithIsDoneValue = useSelector(state =>
selectNumOfTodosWithIsDoneValue(state, isDone)
)
return <div>{NumOfTodosWithIsDoneValue}</div>
}
export const App = () => {
return (
<>
<span>Number of done todos:</span>
<TodoCounterForIsDoneValue isDone={true} />
</>
)
}
Однако, если селектор используется в нескольких экземплярах компонента и зависит от свойств компонента, вам необходимо убедиться, что каждый экземпляр компонента имеет свой собственный экземпляр селектора (почему? см.здесь):
import React, { useMemo } from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'
const makeNumOfTodosWithIsDoneSelector = () =>
createSelector(
state => state.todos,
(_, isDone) => isDone,
(todos, isDone) => todos.filter(todo => todo.isDone === isDone).length
)
export const TodoCounterForIsDoneValue = ({ isDone }) => {
const selectNumOfTodosWithIsDone = useMemo(
makeNumOfTodosWithIsDoneSelector,
[]
)
const numOfTodosWithIsDoneValue = useSelector(state =>
selectNumOfTodosWithIsDoneValue(state, isDone)
)
return <div>{numOfTodosWithIsDoneValue}</div>
}
export const App = () => {
return (
<>
<span>Number of done todos:</span>
<TodoCounterForIsDoneValue isDone={true} />
<span>Number of unfinished todos:</span>
<TodoCounterForIsDoneValue isDone={false} />
</>
)
}
useDispatch()
const dispatch = useDispatch()
Этот хук возвращает пару в магазин Reduxdispatch
Ссылка на функцию. Вы можете использовать его по мере необходимости.
Использование такое же, как и раньше, давайте посмотрим на пример:
import React from 'react'
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch()
return (
<div>
<span>{value}</span>
<button onClick={() => dispatch({ type: 'increment-counter' })}>
Increment counter
</button>
</div>
)
}
когда используешьdispatch
При передаче обратных вызовов дочерним компонентам рекомендуется использоватьuseCallback
Запомните его, иначе дочерние компоненты могут отрисовываться без необходимости из-за изменений ссылок.
import React, { useCallback } from 'react'
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch()
const incrementCounter = useCallback(
() => dispatch({ type: 'increment-counter' }),
[dispatch]
)
return (
<div>
<span>{value}</span>
<MyIncrementButton onIncrement={incrementCounter} />
</div>
)
}
export const MyIncrementButton = React.memo(({ onIncrement }) => (
<button onClick={onIncrement}>Increment counter</button>
))
useStore()
const store = useStore()
Этот хук возвращает избыточность<Provider>
компонентstore
ссылка на объект.
Этот крючок нельзя использовать долго.useSelector
Должен быть вашим первым выбором. Тем не менее, это также иногда полезно. Давайте посмотрим пример:
import React from 'react'
import { useStore } from 'react-redux'
export const CounterComponent = ({ value }) => {
const store = useStore()
// 仅仅是个例子! 不要在你的应用中这样做.
// 如果store中的state改变,这个将不会自动更新
return <div>{store.getState()}</div>
}
представление
Как упоминалось ранее, изменение значения селектора вызовет повторный рендеринг. Но это сconnect
немного отличается,useSelector()
Не предотвращает повторный рендеринг компонента из-за его родительского повторного рендеринга, даже если реквизиты компонента не изменились.
Если требуется дальнейшая оптимизация производительности, вы можетеReact.memo()
Оберните функциональный компонент в:
const CounterComponent = ({ name }) => {
const counter = useSelector(state => state.counter)
return (
<div>
{name}: {counter}
</div>
)
}
export const MemoizedCounterComponent = React.memo(CounterComponent)
Рецепт крючков
Рецепт блюда:useActions()
Это хук для альфы, но слушает в альфе.4предложение Дэнабыл удален. Эта рекомендация основана на том факте, что «создатели действий привязки» не особенно полезны в случаях использования на основе ловушек и вызывают слишком много концептуальных накладных расходов и сложности синтаксиса.
вы можете предпочесть использовать напрямуюuseDispatch. Вы также можете использовать редукциюbindActionCreators
функцию или свяжите их вручную, например:const boundAddTodo = (text) => dispatch(addTodo(text))
.
Однако, если вы все же хотите использовать этот хук самостоятельно, есть готовая версия, поддерживающая передачу в создателя действия в виде отдельной функции, массива или объекта.
import { bindActionCreators } from 'redux'
import { useDispatch } from 'react-redux'
import { useMemo } from 'react'
export function useActions(actions, deps) {
const dispatch = useDispatch()
return useMemo(() => {
if (Array.isArray(actions)) {
return actions.map(a => bindActionCreators(a, dispatch))
}
return bindActionCreators(actions, dispatch)
}, deps ? [dispatch, ...deps] : deps)
}
Рецепт блюда:useShallowEqualSelector()
import { shallowEqual } from 'react-redux'
export function useShallowEqualSelector(selector) {
return useSelector(selector, shallowEqual)
}
использовать
Теперь в компоненте hooks нам не нужно писатьconnect
, не надо писатьmapStateToProps
, тоже не пишиmapDispatchToProps
, просто нужен одинuseSelector
.
вопрос
Вас не устраивает эта версия?
оригинал:Краткая книга: API react-redux@7.1 для хуков
Комментарии к коду:v7.1 code