connect: a Higher-Order Component to interact with Redux">connect: a Higher-Order Component to interact with Redux">
跳至主要内容

connect()

提示

connect 仍然有效,并且在 React-Redux 8.x 中受支持。但是,我们建议使用 Hooks API 作为默认方法.

概述

connect() 函数将 React 组件连接到 Redux store。

它为连接的组件提供从 store 中获取所需数据的片段,以及用于向 store 派发操作的函数。

它不会修改传递给它的组件类;相反,它会返回一个新的、连接的组件类,该类包装了你传递进来的组件。

function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)

mapStateToPropsmapDispatchToProps 分别处理 Redux 存储的 statedispatchstatedispatch 将作为第一个参数提供给你的 mapStateToPropsmapDispatchToProps 函数。

mapStateToPropsmapDispatchToProps 的返回值分别在内部称为 statePropsdispatchProps。如果定义了 mergeProps,它们将作为第一个和第二个参数提供给 mergeProps,其中第三个参数将是 ownProps。组合的结果,通常称为 mergedProps,将被提供给你的连接组件。

connect() 参数

connect 接受四个不同的参数,所有参数都是可选的。按照惯例,它们被称为

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

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

如果指定了 mapStateToProps 函数,新的包装组件将订阅 Redux 存储更新。这意味着,只要存储更新,就会调用 mapStateToPropsmapStateToProps 的结果必须是一个普通对象,它将被合并到包装组件的 props 中。如果你不想订阅存储更新,请在 mapStateToProps 的位置传递 nullundefined

参数

  1. state: Object
  2. ownProps?: Object

mapStateToProps 函数最多接受两个参数。声明的函数参数数量(也称为元数)会影响它何时被调用。这也决定了函数是否会收到 ownProps。请参阅此处的说明。

state

如果你的 mapStateToProps 函数声明为接受一个参数,它将在每次 store 状态发生变化时被调用,并以 store 状态作为唯一参数。

const mapStateToProps = (state) => ({ todos: state.todos })
ownProps

如果你的 mapStateToProps 函数声明为接受两个参数,它将在每次 store 状态发生变化包装组件接收到新 props 时被调用(基于浅层相等性比较)。它将以 store 状态作为第一个参数,以包装组件的 props 作为第二个参数。

第二个参数通常按照惯例称为 ownProps

const mapStateToProps = (state, ownProps) => ({
todo: state.todos[ownProps.id],
})

返回值

你的 mapStateToProps 函数应该返回一个对象。这个对象,通常称为 stateProps,将被合并为连接组件的 props。如果你定义了 mergeProps,它将作为第一个参数传递给 mergeProps

mapStateToProps 的返回值决定连接组件是否会重新渲染(详情 这里)。

有关 mapStateToProps 的推荐用法,请参考 我们关于使用 mapStateToProps 的指南

你可以将 mapStateToPropsmapDispatchToProps 定义为工厂函数,即你返回一个函数而不是一个对象。在这种情况下,你返回的函数将被视为真正的 mapStateToPropsmapDispatchToProps,并在后续调用中被调用。你可以在 工厂函数 或我们的性能优化指南中看到相关说明。

mapDispatchToProps?: Object | (dispatch, ownProps?) => Object

按照惯例,这个 connect() 的第二个参数被称为 mapDispatchToProps,它可以是一个对象、一个函数,也可以不提供。

你的组件默认情况下会收到 dispatch,即当你没有向 connect() 提供第二个参数时。

// do not pass `mapDispatchToProps`
connect()(MyComponent)
connect(mapState)(MyComponent)
connect(mapState, null, mergeProps, options)(MyComponent)

如果你将 mapDispatchToProps 定义为一个函数,它将最多接受两个参数。

参数

  1. dispatch: Function
  2. ownProps?: Object
dispatch

如果您的 mapDispatchToProps 被声明为一个接受一个参数的函数,它将被赋予您的 storedispatch

const mapDispatchToProps = (dispatch) => {
return {
// dispatching plain actions
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
reset: () => dispatch({ type: 'RESET' }),
}
}
ownProps

如果您的 mapDispatchToProps 函数被声明为接受两个参数,它将被调用,第一个参数为 dispatch,第二个参数为传递给包装组件的 props,并且将在连接的组件接收到新的 props 时重新调用。

第二个参数通常按照惯例称为 ownProps

// binds on component re-rendering
<button onClick={() => this.props.toggleTodo(this.props.todoId)} />

// binds on `props` change
const mapDispatchToProps = (dispatch, ownProps) => ({
toggleTodo: () => dispatch(toggleTodo(ownProps.todoId)),
})

mapDispatchToProps 的声明函数参数数量决定了它们是否接收 ownProps。请参阅 此处 的说明。

返回值

您的 mapDispatchToProps 函数应该返回一个对象。该对象的每个字段都应该是一个函数,调用该函数应该向 store 派发一个 action。

您的 mapDispatchToProps 函数的返回值被视为 dispatchProps。它将被合并为您的连接组件的 props。如果您定义了 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,
// you may return dispatch here
dispatch,
}
}

有关推荐用法的更多详细信息,请参阅 我们关于使用 mapDispatchToProps 的指南

你可以将 mapStateToPropsmapDispatchToProps 定义为工厂函数,即你返回一个函数而不是一个对象。在这种情况下,你返回的函数将被视为真正的 mapStateToPropsmapDispatchToProps,并在后续调用中被调用。你可以在 工厂函数 或我们的性能优化指南中看到相关说明。

对象简写形式

mapDispatchToProps 可以是一个对象,其中每个字段都是一个 action creator

import { addTodo, deleteTodo, toggleTodo } from './actionCreators'

const mapDispatchToProps = {
addTodo,
deleteTodo,
toggleTodo,
}

export default connect(null, mapDispatchToProps)(TodoApp)

在这种情况下,React-Redux 使用 bindActionCreators 将您的 store 的 dispatch 绑定到每个 action creator。结果将被视为 dispatchProps,它将直接合并到您的连接组件中,或者作为第二个参数传递给 mergeProps

// internally, React-Redux calls bindActionCreators
// to bind the action creators to the dispatch of your store
bindActionCreators(mapDispatchToProps, dispatch)

我们还在 mapDispatchToProps 指南中有一个关于对象简写形式用法的部分,请参阅 此处

mergeProps?: (stateProps, dispatchProps, ownProps) => Object

如果指定,则定义如何确定您自己的包装组件的最终 props。如果您没有提供 mergeProps,您的包装组件默认情况下会收到 { ...ownProps, ...stateProps, ...dispatchProps }

参数

mergeProps 应该最多指定三个参数。它们分别是 mapStateToProps()mapDispatchToProps() 和包装组件的 props 的结果。

  1. stateProps
  2. dispatchProps
  3. ownProps

从该函数返回的普通对象中的字段将用作包装组件的 props。您可以指定此函数来根据 props 选择状态的一部分,或将动作创建者绑定到 props 中的特定变量。

返回值

mergeProps 的返回值称为 mergedProps,其字段将用作包装组件的 props。

注意:在 mergeProps 中创建新值会导致重新渲染。建议您记忆字段以避免不必要的重新渲染。

options?: Object

{
context?: Object,
areStatesEqual?: Function,
areOwnPropsEqual?: Function,
areStatePropsEqual?: Function,
areMergedPropsEqual?: Function,
forwardRef?: boolean,
}

context: Object

注意:此参数仅在 >= v6.0 中受支持。

React-Redux v6 允许您提供一个自定义上下文实例,供 React-Redux 使用。您需要将您的上下文实例传递给 <Provider /> 和您的连接组件。您可以通过将上下文作为选项的字段传递到这里,或在渲染时作为 props 传递给您的连接组件,将上下文传递给您的连接组件。

// const MyContext = React.createContext();
connect(mapStateToProps, mapDispatchToProps, null, { context: MyContext })(
MyComponent,
)

areStatesEqual: (next: Object, prev: Object, nextOwnProps: Object, prevOwnProps: Object) => boolean

  • 默认值:strictEqual: (next, prev) => prev === next

将传入的存储状态与其先前值进行比较。

const areStatesEqual = (next, prev) =>
prev.entities.todos === next.entities.todos

如果您的 mapStateToProps 函数计算量很大,并且只关心状态的一小部分,您可能希望覆盖 areStatesEqual。上面的示例将有效地忽略除该状态片段之外的所有状态更改。此外,areStatesEqual 提供 nextOwnPropsprevOwnProps,以便在需要时更有效地对连接组件感兴趣的状态进行范围限定。

这可能会影响其他相等性检查,具体取决于您的 mapStateToProps 函数。

areOwnPropsEqual: (next: Object, prev: Object) => boolean

  • 默认值:shallowEqual: (objA, objB) => boolean(当对象的每个字段相等时返回 true

将传入的道具与其先前值进行比较。

您可能希望覆盖 areOwnPropsEqual 作为一种白名单传入道具的方法。您还需要实现 mapStateToPropsmapDispatchToPropsmergeProps 来白名单道具。(通过其他方式实现这一点可能更简单,例如使用 recompose 的 mapProps。)

areStatePropsEqual: (next: Object, prev: Object) => boolean

  • 类型:function
  • 默认值:shallowEqual

mapStateToProps 的结果与其先前值进行比较。

areMergedPropsEqual: (next: Object, prev: Object) => boolean

  • 默认值:shallowEqual

mergeProps 的结果与其先前值进行比较。

如果您的 mapStateToProps 使用一个记忆化的选择器,该选择器只会在相关道具发生更改时返回一个新对象,您可能希望覆盖 areStatePropsEqual 以使用 strictEqual。这将是一个非常小的性能提升,因为它会避免每次调用 mapStateToProps 时对单个道具进行额外的相等性检查。

如果您的选择器生成复杂的道具,您可能希望覆盖 areMergedPropsEqual 以实现 deepEqual。例如:嵌套对象、新数组等。(深度相等检查可能比重新渲染更快。)

forwardRef: boolean

注意:此参数仅在 >= v6.0 中受支持。

如果 {forwardRef : true} 已传递给 connect,则向连接的包装组件添加 ref 实际上将返回包装组件的实例。

connect() 返回值

connect() 的返回值是一个包装函数,它接收你的组件并返回一个包含注入的额外 props 的包装组件。

import { login, logout } from './actionCreators'

const mapState = (state) => state.user
const mapDispatch = { login, logout }

// first call: returns a hoc that you can use to wrap any component
const connectUser = connect(mapState, mapDispatch)

// second call: returns the wrapper component with mergedProps
// you may use the hoc to enable different components to get the same behavior
const ConnectedUserLogin = connectUser(Login)
const ConnectedUserProfile = connectUser(Profile)

在大多数情况下,包装函数会立即被调用,而不会保存在临时变量中。

import { login, logout } from './actionCreators'

const mapState = (state) => state.user
const mapDispatch = { login, logout }

// call connect to generate the wrapper function, and immediately call
// the wrapper function to generate the final wrapper component.

export default connect(mapState, mapDispatch)(Login)

示例用法

由于 connect 非常灵活,因此查看一些关于如何调用它的额外示例可能会有所帮助。

  • 仅注入 dispatch 而不监听 store
export default connect()(TodoApp)
  • 注入所有 action creators (addTodo, completeTodo, ...) 而不订阅 store
import * as actionCreators from './actionCreators'

export default connect(null, actionCreators)(TodoApp)
  • 注入 dispatch 和全局状态中的所有字段

不要这样做!这会破坏任何性能优化,因为 TodoApp 会在每次状态更改后重新渲染。最好在视图层次结构中的多个组件上使用更细粒度的 connect(),每个组件只监听相关状态片段。

// don't do this!
export default connect((state) => state)(TodoApp)
  • 注入 dispatchtodos
function mapStateToProps(state) {
return { todos: state.todos }
}

export default connect(mapStateToProps)(TodoApp)
  • 注入 todos 和所有 action creators
import * as actionCreators from './actionCreators'

function mapStateToProps(state) {
return { todos: state.todos }
}

export default connect(mapStateToProps, actionCreators)(TodoApp)
  • todos 和所有 action creators (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 和一个特定的 action creator (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 和特定的 action creators (addTododeleteTodo)
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,以及 todoActionCreatorscounterActionCreators 一起注入为 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,以及所有 todoActionCreatorscounterActionCreators 注入为 props
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)
  • 根据 props 注入特定用户的 todos
import * as actionCreators from './actionCreators'

function mapStateToProps(state, ownProps) {
return { todos: state.todos[ownProps.userId] }
}

export default connect(mapStateToProps)(TodoApp)
  • 根据 props 注入特定用户的 todos,并将 props.userId 注入到 action 中
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 函数的元数

mapStateToPropsmapDispatchToProps 的声明函数参数数量决定了它们是否接收 ownProps

注意:如果函数的正式定义包含一个必填参数(函数长度为 1),则不会将 ownProps 传递给 mapStateToPropsmapDispatchToProps。例如,以下定义的函数不会将 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) // {}
}

没有必填参数或有两个参数的函数*将接收 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
}

工厂函数

如果您的 mapStateToPropsmapDispatchToProps 函数返回一个函数,它们将在组件实例化时被调用一次,并且它们的返回值将分别用作实际的 mapStateToPropsmapDispatchToProps 函数,在它们后续的调用中。

工厂函数通常与记忆选择器一起使用。这使您能够在闭包内创建特定于组件实例的选择器。

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)

旧版本文档

虽然 connect API 在我们所有主要版本之间几乎完全保持 API 兼容性,但从一个版本到另一个版本,选项和行为有一些细微的变化。

有关旧版 5.x 和 6.x 版本的详细信息,请参阅 React Redux 存储库中的这些存档文件。