connect()
connect
仍然有效,并且在 React-Redux 8.x 中受支持。但是,我们建议使用 Hooks API 作为默认方法.
概述
connect()
函数将 React 组件连接到 Redux store。
它为连接的组件提供从 store 中获取所需数据的片段,以及用于向 store 派发操作的函数。
它不会修改传递给它的组件类;相反,它会返回一个新的、连接的组件类,该类包装了你传递进来的组件。
function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)
mapStateToProps
和 mapDispatchToProps
分别处理 Redux 存储的 state
和 dispatch
。state
和 dispatch
将作为第一个参数提供给你的 mapStateToProps
或 mapDispatchToProps
函数。
mapStateToProps
和 mapDispatchToProps
的返回值分别在内部称为 stateProps
和 dispatchProps
。如果定义了 mergeProps
,它们将作为第一个和第二个参数提供给 mergeProps
,其中第三个参数将是 ownProps
。组合的结果,通常称为 mergedProps
,将被提供给你的连接组件。
connect()
参数
connect
接受四个不同的参数,所有参数都是可选的。按照惯例,它们被称为
mapStateToProps?: Function
mapDispatchToProps?: Function | Object
mergeProps?: Function
options?: Object
mapStateToProps?: (state, ownProps?) => Object
如果指定了 mapStateToProps
函数,新的包装组件将订阅 Redux 存储更新。这意味着,只要存储更新,就会调用 mapStateToProps
。mapStateToProps
的结果必须是一个普通对象,它将被合并到包装组件的 props 中。如果你不想订阅存储更新,请在 mapStateToProps
的位置传递 null
或 undefined
。
参数
state: Object
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
的指南。
你可以将
mapStateToProps
和mapDispatchToProps
定义为工厂函数,即你返回一个函数而不是一个对象。在这种情况下,你返回的函数将被视为真正的mapStateToProps
或mapDispatchToProps
,并在后续调用中被调用。你可以在 工厂函数 或我们的性能优化指南中看到相关说明。
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
定义为一个函数,它将最多接受两个参数。
参数
dispatch: Function
ownProps?: Object
dispatch
如果您的 mapDispatchToProps
被声明为一个接受一个参数的函数,它将被赋予您的 store
的 dispatch
。
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
的指南。
你可以将
mapStateToProps
和mapDispatchToProps
定义为工厂函数,即你返回一个函数而不是一个对象。在这种情况下,你返回的函数将被视为真正的mapStateToProps
或mapDispatchToProps
,并在后续调用中被调用。你可以在 工厂函数 或我们的性能优化指南中看到相关说明。
对象简写形式
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
的结果。
stateProps
dispatchProps
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
提供 nextOwnProps
和 prevOwnProps
,以便在需要时更有效地对连接组件感兴趣的状态进行范围限定。
这可能会影响其他相等性检查,具体取决于您的 mapStateToProps
函数。
areOwnPropsEqual: (next: Object, prev: Object) => boolean
- 默认值:
shallowEqual: (objA, objB) => boolean
(当对象的每个字段相等时返回true
)
将传入的道具与其先前值进行比较。
您可能希望覆盖 areOwnPropsEqual
作为一种白名单传入道具的方法。您还需要实现 mapStateToProps
、mapDispatchToProps
和 mergeProps
来白名单道具。(通过其他方式实现这一点可能更简单,例如使用 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)
- 注入
dispatch
和todos
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 (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
注入为 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
函数的元数
mapStateToProps
和 mapDispatchToProps
的声明函数参数数量决定了它们是否接收 ownProps
注意:如果函数的正式定义包含一个必填参数(函数长度为 1),则不会将
ownProps
传递给mapStateToProps
和mapDispatchToProps
。例如,以下定义的函数不会将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
}
工厂函数
如果您的 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)
旧版本文档
虽然 connect
API 在我们所有主要版本之间几乎完全保持 API 兼容性,但从一个版本到另一个版本,选项和行为有一些细微的变化。
有关旧版 5.x 和 6.x 版本的详细信息,请参阅 React Redux 存储库中的这些存档文件。