connect()
Подсказка
connect
всё ещё работает и поддерживается в React-Redux версии 8.x. Как бы то ни было, мы настоятельно рекомендуем по умолчанию использовать хуки.
Введение
Функция connect()
соединяет компонент React с Redux хранилищем(store).
Она возвращает подключенный компонент с необходимой ему частью состояния и функция для отправки(dispatch) действий в хранилище(store).
Она не изменяет получаемый на вход компонент; вместо этого, она возвращает новый компонент-обёртку, подключенный к хранилищу(store) и содержащий пользовательский компонент.
function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)
Функции mapStateToProps
и mapDispatchToProps
работают с состоянием state
Redux хранилища(store) и отправкой действий dispatch
соотвественно. state
и dispatch
будут поставляться первым параметром в функции mapStateToProps
или mapDispatchToProps
.
Возвращаемые значения из функций mapStateToProps
и mapDispatchToProps
именуются как stateProps
и dispatchProps
, соответственно. Если они определены, то они будут поставляться в mergeProps
как первый и второй параметр, в то время как третьим параметром будет ownProps
. Скомбинированный результат обычно именуется как mergedProps
и будет поставляться в ваш подключенный компонент.
Параметры connect()
connect
принимает 4 разных необязательных параметра. По конвенции они называются:
mapStateToProps?: Function
mapDispatchToProps?: Function | Object
mergeProps?: Function
options?: Object
mapStateToProps?: (state, ownProps?) => Object
Если функция mapStateToProps
определена, новый компонент-обёртка будет подписываться на обновления Redux хранилища(store). Это означает, что при каждом обновлении хранилища(store) будет вызываться mapStateToProps
. Результатом mapStateToProps
должен быть простым объектом, который будет объединён с пропсами обёрнутого компонента. Если вы не хотите подписываться на обновления хранилища(store), укажите null
или undefined
вместо mapStateToProps
.
Параметры
state: Object
ownProps?: Object
Функция mapStateToProps
принимает максимум 2 параметра. Количество объявленных параметров функции (т.н. арность) влияет на то, когда функция будет вызвана. Это также определяет получит ли функция ownProps
. Смотрите записи здесь.
state
Если ваша функция mapStateToProps
объявлена с 1 параметром, она будет вызываться при каждом изменении в хранилище(store) и получать аргументом актуальное состояние хранилище в качестве.
const mapStateToProps = (state) => ({ todos: state.todos })
ownProps
Если ваша функция mapStateToProps
объявлена с 2-мя параметрами, она будет вызываться при каждом изменении состояния хранилища(store) или когда компонент-обёртка получит новые пропсы (основываясь на неглубокой проверке на равенство). Она получит состояние хранилища(store) первым аргументом и пропсы компонента-обёртки вторым аргументом.
Второй параметр по конвенции называется ownProps
.
const mapStateToProps = (state, ownProps) => ({
todo: state.todos[ownProps.id],
})
Возвращаемое значение
Ожидается, что ваша функция mapStateToProps
вернёт объект. Этот объект обычно называется как stateProps
и он будет объединен с пропсам вашего подключаемого компонента. Если вы укажете параметр mergeProps
, он будет поставляться первым аргументом в mergeProps
.
Возвращаемое значение mapStateToProps
определяет будет ли подключенный компонент перерисовываться (детали здесь).
За подробностями использования mapStateToProps
мы рекомендуем обращайться к нашему гайду по использованию mapStateToProps
.
Вы можете определить
mapStateToProps
иmapDispatchToProps
как фабричную функцию, т.е. вы вернёте функцию вместо объекта. В этом случае возвращаемая функция будет рассматриваться в качестве настоящего аргументаmapStateToProps
илиmapDispatchToProps
, и будет вызываться при последующих вызовах. Вы можете посмотреть записи про фабричные функции или наш гайд на оптимизацию производительности.
mapDispatchToProps?: Object | (dispatch, ownProps?) => Object
По конвенции mapDispatchToProps
- второй параметр функции connect()
также может быть объектом, функцией или быть не указан вовсе.
Ваш компонент будет получать функцию dispatch
по умолчанию, т.е. когда вы не указываете второй аргумент для connect()
:
// не указываем `mapDispatchToProps`
connect()(MyComponent)
connect(mapState)(MyComponent)
connect(mapState, null, mergeProps, options)(MyComponent)
Если вы определите mapDispatchToProps
как функцию, она будет вызываться с максимум 2 аргументами.
Параметры
dispatch: Function
ownProps?: Object
dispatch
Если ваш mapDispatchToProps
объявлен как функция, принимающая 1 параметр, она получит dispatch
вашего store
.
const mapDispatchToProps = (dispatch) => {
return {
// отправляем(dispatch) простые действия(action)
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
reset: () => dispatch({ type: 'RESET' }),
}
}
ownProps
Если ваша функция mapDispatchToProps
объявлена с 2-мя параметрами, она будет вызываться с dispatch
первым аргументом и с пропсами, переданными компоненту-оболочке, в качестве второго аргумента и будет повторно вызваться, когда подключенный компонент получает новые пропсы.
Второй параметр по конвенции называется ownProps
.
// привязываемся к повторному рендерингу компонента
<button onClick={() => this.props.toggleTodo(this.props.todoId)} />
// Привязываемся к изменению `props`
const mapDispatchToProps = (dispatch, ownProps) => ({
toggleTodo: () => dispatch(toggleTodo(ownProps.todoId)),
})
Количество определённых параметров функции mapDispatchToProps
определяет получит ли она ownProps. Смотрите записи здесь.
Возвращаемое значение
Ожидается, что ваша функция mapDispatchToProps
вернёт объект. Каждое из полей объекта следует указывать функцией, вызывая которую ожидается отправка(dispatch) действия(action) в хранилище(store).
Возвращаемые функции из mapDispatchToProps
расцениваться как dispatchProps
. Они будут объединены с пропсам вашего подключаемого компонента. Если вы укажете параметр mergeProps
, то он будет поставляться вторым аргументом в mergeProps
.
const createMyAction = () => ({ type: 'MY_ACTION' })
const mapDispatchToProps = (dispatch, ownProps) => {
const boundActions = bindActionCreators({ createMyAction }, dispatch)
return {
dispatchPlainObject: () => dispatch({ type: 'MY_ACTION' }),
dispatchActionCreatedByActionCreator: () => dispatch(createMyAction()),
...boundActions,
// здесь вы также можете вернуть dispatch
dispatch,
}
}
За подробностями использования mapDispatchToProps
мы рекомендуем обращайться к нашему гайду по использованию mapDispatchToProps
.
Вы можете определить
mapStateToProps
иmapDispatchToProps
как фабричную функцию, т.е. вы вернёте функцию вместо объекта. В этом случае возвращаемая функция будет рассматриваться в качестве настоящего аргументаmapStateToProps
илиmapDispatchToProps
, и будет вызываться при последующих вызовах. Вы можете посмотреть записи про фабричные функции или наш гайд на оптимизацию производительности.
Сокращенная форма объекта
mapDispatchToProps
может быть объектом, где каждое поле является создателем действия.
import { addTodo, deleteTodo, toggleTodo } from './actionCreators'
const mapDispatchToProps = {
addTodo,
deleteTodo,
toggleTodo,
}
export default connect(null, mapDispatchToProps)(TodoApp)
В этом случае React-Redux привязывает dispatch
к каждому создателю действий, используя bindActionCreators
, результат будет расцениваться как dispatchProps
, которые также будут объединены с вашими подключенными компонентами или переданы в mergeProps
вторым аргументом.
// Под капотом React-Redux вызывает bindActionCreators
// для привязки создателей действий к dispatch вашего хранилища(store)
bindActionCreators(mapDispatchToProps, dispatch)
У нас также есть секция про mapDispatchToProps
в гайде про использование сокращенной формы объекта здесь.
mergeProps?: (stateProps, dispatchProps, ownProps) => Object
Если параметр указан, определяет окончательный вид пропсов для вашего компонента-обёртки. Если вы не предоставите mergeProps
, ваш компонент-обёртка по умолчанию получит { ...ownProps, ...stateProps, ...dispatchProps }
.
Параметры
В функцию mergeProps
указывается максимум 3 параметра. Они являются результатами mapStateToProps()
, mapDispatchToProps()
и props
компонента-обёртки соответственно:
stateProps
dispatchProps
ownProps
Поля объекта, возвращаемого из неё, будут использоваться как пропсы для компонента-обёртки. Вы можете указать функцию mergeProps
, чтобы выбрать часть состояния(slice), основываясь на пропсах или чтобы привязать создателей действий к определённой переменной из пропсов.
Возвращаемое значение
Возвращаемое значение из функции mergeProps
именуется mergedProps
и его поля будут передаются как пропсы для компонента-обёртки.
Примечание: создание новых значений в функции mergeProps будет вызывать повторные отрисовки. Рекомендуется мемоизировать поля с целью избежать ненужных перерисовок.
options?: Object
{
context?: Object,
areStatesEqual?: Function,
areOwnPropsEqual?: Function,
areStatePropsEqual?: Function,
areMergedPropsEqual?: Function,
forwardRef?: boolean,
}
context: Object
Примечание: этот параметр поддерживается только начиная с версии 6.0
React-Redux версии 6 позволяет вам передать пользовательский экземпляр контекста, используемый React-Redux'ом.
Вам нужно передать экземпляр вашего контекста и в <Provider />
, и в ваш подключенный компонент.
Вы можете передать контекст подключенному компоненту, указав его в поле параметра connect()
, либо в качестве пропса к подключенному компоненту при рендеринге.
// const MyContext = React.createContext();
connect(mapStateToProps, mapDispatchToProps, null, { context: MyContext })(
MyComponent
)
areStatesEqual: (next: Object, prev: Object) => boolean
- Значение по умолчанию:
strictEqual: (next, prev) => prev === next
Сравнивает входящее состояние хранилища(store) с его предыдущим состоянием.
const areStatesEqual = (next, prev) =>
prev.entities.todos === next.entities.todos
Вам может понадобиться переопределить areStatesEqual
, если ваша функция mapStateToProps
содержит сложные вычислениях или если она касается только небольшой части вашего состояния. Пример выше эффективен: игнорируются все изменения состояния, не касающиеся используемой части(slice) состояния.
Это, вероятно, повлияет и на другие проверки на равенство, в зависимости от вашей функции mapStateToProps
.
areOwnPropsEqual: (next: Object, prev: Object) => boolean
- значение по умолчанию:
shallowEqual: (objA, objB) => boolean
( возвращаетtrue
, когда все поля объектов соответственно равны между собой )
Сравнивает входящие пропсы и их предыдущее значение.
Вы можете переопределить areOwnPropsEqual
чтобы заносить входящие пропсы в "белый список". Для этого Вам также нужно будет реализовать mapStateToProps
, mapDispatchToProps
и mergeProps
. (Возможно, проще будет использовать другой способ, например recompose's mapProps.)
areStatePropsEqual: (next: Object, prev: Object) => boolean
- Тип:
function
- Значение по умолчанию:
shallowEqual
Сравнивает результат функции mapStateToProps
с его предыдущим значением.
areMergedPropsEqual: (next: Object, prev: Object) => boolean
- Значение по умолчанию:
shallowEqual
Сравнивает результат функции mergeProps
с его предыдущим значением.
Вы можете переопределить areStatePropsEqual
, использовать strictEqual
, если ваш mapStateToProps
использует мемоизированный селектор, возвращающий новый объект, если соответствующее свойство изменилось. Это может незначительно улучшить производительность, так как это позволит избежать дополнительных проверок на равенство для отдельных пропсов при каждом вызове mapStateToProps
.
Вы можете переопределить areMergedPropsEqual
для реализации deepEqual
, если ваши селекторы представляют собой сложные свойства, например: вложенные объекты, новые массивы итд (Глубокая проверка на равенство может быть быстрее, чем повторная отрисовка.)
forwardRef: boolean
Примечание: этот параметр поддерживается только начиная с версии 6.0
Если указано значение {forwardRef : true}
в connect
, то добавление ссылки на подключенный компонент-обёртку фактически вернёт экземпляр компонента-обёртки.
Возвращаемое значение connect()
-->
connect()
возвращает функцию для обёртки, принимающую в себя компонент и возвращает компонент-обёртку с дополнительными пропсами.
import { login, logout } from './actionCreators'
const mapState = (state) => state.user
const mapDispatch = { login, logout }
// Первый вызов: возвращает компонент высшего порядка,
// который вы можете использовать, чтобы обернуть любой компонент
const connectUser = connect(mapState, mapDispatch)
// Второй вызов: возвращает компонент-обёртку с mergedProps.
// Вы можете использовать компонент высшего порядка,
// чтобы дать разным компонентам одинаковое поведение
const ConnectedUserLogin = connectUser(Login)
const ConnectedUserProfile = connectUser(Profile)
В большинстве случаев функция для обёртки вызывается сразу, без временной переменной:
import { login, logout } from './actionCreators'
const mapState = (state) => state.user
const mapDispatch = { login, logout }
// вызов connect для создания функции обёртки и незамедлительный вызов
// этой функции для генерации компонента-обёртки.
export default connect(mapState, mapDispatch)(Login)
Пример использования
В силу гибкости connect
, могут понадобиться дополнительные примеры того, как его можно вызвать:
- Внедрить только
dispatch
и не подписываться на хранилище(store)
export default connect()(TodoApp)
- Внедрение всех создателей действий (
addTodo
,completeTodo
, ...) без подписки на хранилище(store)
import * as actionCreators from './actionCreators'
export default connect(null, actionCreators)(TodoApp)
- Внедрение
dispatch
и каждого поля в глобальном состоянии(state)
Не делайте этого! Это убивает любые оптимизации производительность, поскольку
TodoApp
будет перерисовываться после каждого изменения состояния.Лучше иметь точечный вызов
connect()
в нескольких компонентах в вашей иерархии, которые будут подписываться только на релевантные части общего состояния(state).
// Не делайте этого!
export default connect((state) => state)(TodoApp)
- Внедрение
dispatch
иtodos
function mapStateToProps(state) {
return { todos: state.todos }
}
export default connect(mapStateToProps)(TodoApp)
- Внедрение
todos
и всех создателей действий
import * as actionCreators from './actionCreators'
function mapStateToProps(state) {
return { todos: state.todos }
}
export default connect(mapStateToProps, actionCreators)(TodoApp)
- Внедрение
todos
и всех создателей действий (addTodo
,completeTodo
, ...) какactions
import * as actionCreators from './actionCreators'
import { bindActionCreators } from 'redux'
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return { actions: bindActionCreators(actionCreators, dispatch) }
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)
- Внедрение
todos
и определённого создателя действия (addTodo
)
import { addTodo } from './actionCreators'
import { bindActionCreators } from 'redux'
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ addTodo }, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)
- Внедрение
todos
и определённых создателей действий (addTodo
иdeleteTodo
), используя сокращенный синтаксис
import { addTodo, deleteTodo } from './actionCreators'
function mapStateToProps(state) {
return { todos: state.todos }
}
const mapDispatchToProps = {
addTodo,
deleteTodo,
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)
- Внедрение
todos
,todoActionCreators
какtodoActions
иcounterActionCreators
какcounterActions
import * as todoActionCreators from './todoActionCreators'
import * as counterActionCreators from './counterActionCreators'
import { bindActionCreators } from 'redux'
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return {
todoActions: bindActionCreators(todoActionCreators, dispatch),
counterActions: bindActionCreators(counterActionCreators, dispatch),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)
- Внедрение
todos
и todoActionCreators вместе с counterActionCreators какactions
import * as todoActionCreators from './todoActionCreators'
import * as counterActionCreators from './counterActionCreators'
import { bindActionCreators } from 'redux'
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(
{ ...todoActionCreators, ...counterActionCreators },
dispatch
),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)
- Внедрение
todos
и всех создателей действийtodoActionCreators
иcounterActionCreators
напрямую в пропсы
import * as todoActionCreators from './todoActionCreators'
import * as counterActionCreators from './counterActionCreators'
import { bindActionCreators } from 'redux'
function mapStateToProps(state) {
return { todos: state.todos }
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{ ...todoActionCreators, ...counterActionCreators },
dispatch
)
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)
- Внедрение
todos
определённого пользователя в зависимости от пропсов
import * as actionCreators from './actionCreators'
function mapStateToProps(state, ownProps) {
return { todos: state.todos[ownProps.userId] }
}
export default connect(mapStateToProps)(TodoApp)
- Внедрение
todos
определённого пользователя в зависимости от пропсов и внедрениеprops.userId
в действие
import * as actionCreators from './actionCreators'
function mapStateToProps(state) {
return { todos: state.todos }
}
function mergeProps(stateProps, dispatchProps, ownProps) {
return Object.assign({}, ownProps, {
todos: stateProps.todos[ownProps.userId],
addTodo: (text) => dispatchProps.addTodo(ownProps.userId, text),
})
}
export default connect(mapStateToProps, actionCreators, mergeProps)(TodoApp)
Примечания
Арность функций mapToProps
Количество объявленных параметров функций mapStateToProps
и mapDispatchToProps
определяет, получат они в аргументы ownProps
или нет.
Примечание:
ownProps
не передаётся вmapStateToProps
иmapDispatchToProps
, если формальное определение функции содержит один обязательный параметр (функция имеет длину 1). Например, функции определённые внизу не получатownProps
вторым аргументом. Если входящее значениеownProps
этоundefined
, будет использоваться значение аргумента по умолчанию.
function mapStateToProps(state) {
console.log(state) // state
console.log(arguments[1]) // undefined
}
const mapStateToProps = (state, ownProps = {}) => {
console.log(state) // state
console.log(ownProps) // {}
}
Функции без обязательных параметров или с 2 параметрами*будут получать ownProps
.
const mapStateToProps = (state, ownProps) => {
console.log(state) // state
console.log(ownProps) // ownProps
}
function mapStateToProps() {
console.log(arguments[0]) // state
console.log(arguments[1]) // ownProps
}
const mapStateToProps = (...args) => {
console.log(args[0]) // state
console.log(args[1]) // ownProps
}
Фабричные функции
Если ваши mapStateToProps
или mapDispatchToProps
возвращают функцию, они будут вызываться единожды при инициализации компонента и их возвращаемая функция будет фактическим значением функций mapStateToProps
и mapDispatchToProps
соответственно в их последующих вызовах.
Фабричные функции обычно используются с мемоизированными селекторами. Это даёт возможность создать селекторы, специфичные для экземпляра компонента, внутри замыкания:
const makeUniqueSelectorInstance = () =>
createSelector([selectItems, selectItemId], (items, itemId) => items[itemId])
const makeMapState = (state) => {
const selectItemForThisComponent = makeUniqueSelectorInstance()
return function realMapState(state, ownProps) {
const item = selectItemForThisComponent(state, ownProps.itemId)
return { item }
}
}
export default connect(makeMapState)(SomeComponent)
Устаревшие версии документации
Пока что API connect
осталось почти полностью совместимым между всеми основными версиями React Redux, были только небольшие изменения в параметрах и поведении от версии к версии.
Подробную информацию об устаревших версиях 5.x и 6.x смотрите в этих архивных файлах в репозитории React Redux: