Skip to main content

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 разных необязательных параметра. По конвенции они называются:

  1. mapStateToProps?: Function
  2. mapDispatchToProps?: Function | Object
  3. mergeProps?: Function
  4. options?: Object

mapStateToProps?: (state, ownProps?) => Object

Если функция mapStateToProps определена, новый компонент-обёртка будет подписываться на обновления Redux хранилища(store). Это означает, что при каждом обновлении хранилища(store) будет вызываться mapStateToProps. Результатом mapStateToProps должен быть простым объектом, который будет объединён с пропсами обёрнутого компонента. Если вы не хотите подписываться на обновления хранилища(store), укажите null или undefined вместо mapStateToProps.

Параметры

  1. state: Object
  2. 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 аргументами.

Параметры

  1. dispatch: Function
  2. 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 компонента-обёртки соответственно:

  1. stateProps
  2. dispatchProps
  3. 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: