API
<Provider store>
使得层级之下的组件可以通过connect()
函数访问到Redux store。通常,如果你不将父组件或者根组件用<Provider>
组件包裹起来的话,你是不可以使用connect()
函数的。
Props
-
store
(Redux Store): 应用中的单一数据源 -
children
(ReactElement) :你的根组件
Example
Vanilla React
ReactDOM.render(
<Provider store={store}>
<MyRootComponent />
</Provider>,
rootEl
)
React Router
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<Route path="foo" component={Foo}/>
<Route path="bar" component={Bar}/>
</Route>
</Router>
</Provider>,
document.getElementById('root')
)
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
将一个React组件连接到Redux store上去。connect
是connectAdvanced
的一层包装,对外提供了一套对于大多数开发情况下方便的API。
它并不更改传入的组件,而是返回一个新的、连接到store的组件共你使用。
Arguments
-
[mapStateToProps(state, [ownProps]): stateProps] (Function):如果这个参数指定了,这个新组件将会订阅store的更新。这意味着每次store更新,
mapStateToProps
将会被调用。mapStateToProps
的结果是一个plain object,这个结果会和组件的props融合为新的props。如果你不想订阅store的更新,传入null或者undefined。如果你的
mapStateToProps
传入了两个参数,那么调用它时,store将会作为第一个参数,connected component的props将会作为第二个参数,并且也会重新触发,如果判定机制——浅相等比较判断组件接收到新的props。注意:在你需要对渲染性能有更多的控制的情况下,
mapStateToProps
也可以返回一个函数。在这种情况下,那个返回的函数将被特定的组件作为mapStateTOProps
。这使得你可以完成单一实例记忆化(per-instance memoization
)You can refer to #279 and the tests it adds for more details. 大多数应用不需要用到它。当
mapStateToProps
接受一个store参数时,它将返回一个对象,这个对象将作为组建的props。我们通常将其称作selector,Use reselect to efficiently compose selectors and compute derived data. -
[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function):如果传入的是object,每一个其中的函数都应该是一个Redux action creator。一个拥有同样的函数的对象将会融合入组件的props,但不同的是这个对象的每个action creator将会作为dispatch的参数,这样它们就可以直接执行。
如果传入的是一个函数,那么这个函数将会接受dispatch作为它的第一个参数。It’s up to you to return an object that somehow uses dispatch
to bind action creators in your own way. (Tip: you may use the bindActionCreators()
helper from Redux.)如果你的
mapDispatchToProps
传入了两个参数,那么dispatch将作为第一个参数,被连接的组件传入的props将作为第二个参数。每当组将接收到新的参数时,这个函数都将被触发。如果你不指定
mapDispatchToProps
参数,那么默认的mapDispatchToProps
实现会将dispatch作为props连接的组件。Note: in advanced scenarios where you need more control over the rendering performance, mapDispatchToProps()
can also return a function. In this case, that function will be used as mapDispatchToProps()
for a particular component instance. This allows you to do per-instance memoization. You can refer to #279 and the tests it adds for more details. Most apps never need this. -
[mergeProps(stateProps, dispatchProps, ownProps): props] (Function):如果这个函数指定了,那么传入这个函数的前两个参数分别为
mapStateToProps(), mapDispatchToProps()
的执行结果,第三个参数为组件自身的props。这个函数将会返回一个plain object,这个结果将成为被包裹组件的props。你也许可以用这个回调函数,根据组件的 props 来筛选部分的 state 数据,或者把 props 中的某个特定变量与 action creator 绑定在一起。如果你省略这个参数,默认情况下返回Object.assign({}, ownProps, stateProps, dispatchProps)
的结果。 -
[options] (Object):如果指定这个参数,可以进一步定制connector的行为。除了
connectAdvanced()
可以传入的选项之外,connect()
还可以接收以下的额外选项。- [
pure
](Boolean):如果为true,当相关的state/props的相等判断机制判断其没有发生变化时,connect()
将会避免组建的重新render()
以及mapStateToProps, mapDispatchToProps, mergeProps
的调用,前提是该组件是一个“纯”组件,即该组件不依赖于任何外部的输入和内部的state,只依赖于props和从connect()
函数获取到的store的数据。默认值为true。
- [
areStatesEqual
] (Function): When pure, compares incoming store state to its previous value. Default value:strictEqual (===)
- [
areOwnPropsEqual
] (Function): When pure, compares incoming props to its previous value. Default value:shallowEqual
- [
areStatePropsEqual
] (Function): When pure, compares the result ofmapStateToProps
to its previous value. Default value:shallowEqual
- [
areMergedPropsEqual
] (Function): When pure, compares the result ofmergeProps
to its previous value. Default value:shallowEqual
- [
storeKey
] (String): The key of the context from where to read the store. You probably only need this if you are in the inadvisable position of having multiple stores. Default value:'store'
- [
mapStateToProps和mapDispatchToProps函数的元数(arity,可以理解为函数长度)决定了它们是否能接收到ownProps作为第二个参数。
注意:如果定义一个包含强制性参数函数(这个函数的长度为 1)时,
ownProps
不会传到mapStateToProps
和mapDispatchToProps
中。举个例子,如下这样定义一个函数时将不会接收到 ownProps 作为第二个参数。
function mapStateToProps(state) {
console.log(state); // state
console.log(arguments[1]); // undefined
}
//因为这个函数有一个参数有默认值,所以这个函数的长度仍然为1
const mapStateToProps = (state, ownProps = {}) => {
console.log(state); // state
console.log(ownProps); // undefined
}
当函数没有强制性的参数或两个参数时将接收到 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
}
Optimizing(优化) connect when options.pure is true
当options.pure
为true
的时候,connect
将执行几个相等检测,以此避免不必要的mapStateToProps, mapDispatchToProps, mergeProps
调用,最终导致render()
。这些相等检测包括areStatesEqual
, areOwnPropsEqual
, areStatePropsEqual
, 和areMergedPropsEqual
。尽管默认参数可能适合于99%的场景,但是出于性能或者其他原因,你也许像自定义它们的实现,下面是几个例子。
-
如果
mapStateToProps
需要花费昂贵的计算时间并且只关心你的状态的一小部分,那么你可以重写areStatesEqual
。举个例子:areStatesEqual: (next, prev) => prev.entities.todos === next.entities.todos
。 -
当你使用不纯的reducers时,你或许希望重写
areStatesEqual
,使它一直返回false。 -
You may wish to override
areOwnPropsEqual
as a way to whitelist incoming props. You'd also have to implementmapStateToProps
,mapDispatchToProps
andmergeProps
to also whitelist props. (It may be simpler to achieve this other ways, for example by using recompose's mapProps.) -
You may wish to override
areStatePropsEqual
to usestrictEqual
if yourmapStateToProps
uses a memoized selector that will only return a new object if a relevant prop has changed. This would be a very slight performance improvement, since would avoid extra equality checks on individual props each timemapStateToProps
is called. -
You may wish to override
areMergedPropsEqual
to implement adeepEqual
if your selectors produce complex props. ex: nested objects, new arrays, etc. (The deep equal check should be faster than just re-rendering.)
返回值
根据配置信息,返回一个注入了 state 和 action creator 的 React高阶组件。这个组件是由connectAdvanced
创建的。
例子
Inject just dispatch
and don't listen to store
export default connect()(TodoApp)
Inject all action creators (addTodo
, completeTodo
, ...) without subscribing to the store
import * as actionCreators from './actionCreators'
export default connect(null, actionCreators)(TodoApp)
Inject dispatch
and every field in the global state
Don’t do this! It kills any performance optimizations because
TodoApp
will rerender after every state change.
It’s better to have more granularconnect()
on several components in your view hierarchy that each only
listen to a relevant slice of the state.
export default connect(state => state)(TodoApp)
Inject dispatch
and todos
function mapStateToProps(state) {
return { todos: state.todos }
}
export default connect(mapStateToProps)(TodoApp)
Inject todos
and all action creators
import * as actionCreators from './actionCreators'
function mapStateToProps(state) {
return { todos: state.todos }
}
export default connect(mapStateToProps, actionCreators)(TodoApp)
Inject todos
and all action creators (addTodo
, completeTodo
, ...) as 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)
Inject todos
and a specific 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)
Inject todos
and specific action creators (addTodo
and deleteTodo
) with shorthand syntax
import { addTodo, deleteTodo } from './actionCreators'
function mapStateToProps(state) {
return { todos: state.todos }
}
const mapDispatchToProps = {
addTodo,
deleteTodo
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)
Inject todos
, todoActionCreators as todoActions
, and counterActionCreators as 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)
Inject todos
, and todoActionCreators and counterActionCreators together as 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(Object.assign({}, todoActionCreators, counterActionCreators), dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)
Inject todos
, and all todoActionCreators and counterActionCreators directly as 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(Object.assign({}, todoActionCreators, counterActionCreators), dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoApp)
Inject todos
of a specific user depending on props
import * as actionCreators from './actionCreators'
function mapStateToProps(state, ownProps) {
return { todos: state.todos[ownProps.userId] }
}
export default connect(mapStateToProps)(TodoApp)
Inject todos
of a specific user depending on props, and inject props.userId
into the 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)
Factory functions
Factory functions can be used for performance optimizations
import { addTodo } from './actionCreators'
function mapStateToPropsFactory(initialState, initialProps) {
const getSomeProperty= createSelector(...);
const anotherProperty = 200 + initialState[initialProps.another];
return function(state){
return {
anotherProperty,
someProperty: getSomeProperty(state),
todos: state.todos
}
}
}
function mapDispatchToPropsFactory(initialState, initialProps) {
function goToSomeLink(){
initialProps.history.push('some/link');
}
return function(dispatch){
return {
addTodo
}
}
}
export default connect(mapStateToPropsFactory, mapDispatchToPropsFactory)(TodoApp)
connectAdvanced(selectorFactory, [connectOptions])
将一个React组件连接到redux store上去。它是connect()
的基础,但是对于如何将state
,props
,dispatch
结合到最终的props上没有那么固定的限制。它不会对默认值或结果的记忆做任何假设,而是将这些责任留给调用者。
Arguments
- selectorFactory(dispatch, factoryOptions): selector(state, ownProps): props (Function):
网友评论