API
<Provider store>
使得層級之下的組件可以通過connect()函數(shù)訪問到Redux store。通常,如果你不將父組件或者根組件用<Provider>組件包裹起來的話,你是不可以使用connect()函數(shù)的。
Props
-
store(Redux Store): 應用中的單一數(shù)據(jù)源 -
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的一層包裝,對外提供了一套對于大多數(shù)開發(fā)情況下方便的API。
它并不更改傳入的組件,而是返回一個新的、連接到store的組件共你使用。
Arguments
-
[mapStateToProps(state, [ownProps]): stateProps] (Function):如果這個參數(shù)指定了,這個新組件將會訂閱store的更新。這意味著每次store更新,
mapStateToProps將會被調用。mapStateToProps的結果是一個plain object,這個結果會和組件的props融合為新的props。如果你不想訂閱store的更新,傳入null或者undefined。如果你的
mapStateToProps傳入了兩個參數(shù),那么調用它時,store將會作為第一個參數(shù),connected component的props將會作為第二個參數(shù),并且也會重新觸發(fā),如果判定機制——淺相等比較判斷組件接收到新的props。注意:在你需要對渲染性能有更多的控制的情況下,
mapStateToProps也可以返回一個函數(shù)。在這種情況下,那個返回的函數(shù)將被特定的組件作為mapStateTOProps。這使得你可以完成單一實例記憶化(per-instance memoization)You can refer to #279 and the tests it adds for more details. 大多數(shù)應用不需要用到它。當
mapStateToProps接受一個store參數(shù)時,它將返回一個對象,這個對象將作為組建的props。我們通常將其稱作selector,Use reselect to efficiently compose selectors and compute derived data. -
[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function):如果傳入的是object,每一個其中的函數(shù)都應該是一個Redux action creator。一個擁有同樣的函數(shù)的對象將會融合入組件的props,但不同的是這個對象的每個action creator將會作為dispatch的參數(shù),這樣它們就可以直接執(zhí)行。
如果傳入的是一個函數(shù),那么這個函數(shù)將會接受dispatch作為它的第一個參數(shù)。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傳入了兩個參數(shù),那么dispatch將作為第一個參數(shù),被連接的組件傳入的props將作為第二個參數(shù)。每當組將接收到新的參數(shù)時,這個函數(shù)都將被觸發(fā)。如果你不指定
mapDispatchToProps參數(shù),那么默認的mapDispatchToProps實現(xiàn)會將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):如果這個函數(shù)指定了,那么傳入這個函數(shù)的前兩個參數(shù)分別為
mapStateToProps(), mapDispatchToProps()的執(zhí)行結果,第三個參數(shù)為組件自身的props。這個函數(shù)將會返回一個plain object,這個結果將成為被包裹組件的props。你也許可以用這個回調函數(shù),根據(jù)組件的 props 來篩選部分的 state 數(shù)據(jù),或者把 props 中的某個特定變量與 action creator 綁定在一起。如果你省略這個參數(shù),默認情況下返回Object.assign({}, ownProps, stateProps, dispatchProps)的結果。-
[options] (Object):如果指定這個參數(shù),可以進一步定制connector的行為。除了
connectAdvanced()可以傳入的選項之外,connect()還可以接收以下的額外選項。- [
pure](Boolean):如果為true,當相關的state/props的相等判斷機制判斷其沒有發(fā)生變化時,connect()將會避免組建的重新render()以及mapStateToProps, mapDispatchToProps, mergeProps的調用,前提是該組件是一個“純”組件,即該組件不依賴于任何外部的輸入和內部的state,只依賴于props和從connect()函數(shù)獲取到的store的數(shù)據(jù)。默認值為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 ofmapStateToPropsto its previous value. Default value:shallowEqual - [
areMergedPropsEqual] (Function): When pure, compares the result ofmergePropsto 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函數(shù)的元數(shù)(arity,可以理解為函數(shù)長度)決定了它們是否能接收到ownProps作為第二個參數(shù)。
注意:如果定義一個包含強制性參數(shù)函數(shù)(這個函數(shù)的長度為 1)時,
ownProps不會傳到mapStateToProps和mapDispatchToProps中。舉個例子,如下這樣定義一個函數(shù)時將不會接收到 ownProps 作為第二個參數(shù)。
function mapStateToProps(state) {
console.log(state); // state
console.log(arguments[1]); // undefined
}
//因為這個函數(shù)有一個參數(shù)有默認值,所以這個函數(shù)的長度仍然為1
const mapStateToProps = (state, ownProps = {}) => {
console.log(state); // state
console.log(ownProps); // undefined
}
當函數(shù)沒有強制性的參數(shù)或兩個參數(shù)時將接收到 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(優(yōu)化) connect when options.pure is true
當options.pure為true的時候,connect將執(zhí)行幾個相等檢測,以此避免不必要的mapStateToProps, mapDispatchToProps, mergeProps調用,最終導致render()。這些相等檢測包括areStatesEqual, areOwnPropsEqual, areStatePropsEqual, 和areMergedPropsEqual。盡管默認參數(shù)可能適合于99%的場景,但是出于性能或者其他原因,你也許像自定義它們的實現(xiàn),下面是幾個例子。
如果
mapStateToProps需要花費昂貴的計算時間并且只關心你的狀態(tài)的一小部分,那么你可以重寫areStatesEqual。舉個例子:areStatesEqual: (next, prev) => prev.entities.todos === next.entities.todos。當你使用不純的reducers時,你或許希望重寫
areStatesEqual,使它一直返回false。You may wish to override
areOwnPropsEqualas a way to whitelist incoming props. You'd also have to implementmapStateToProps,mapDispatchToPropsandmergePropsto also whitelist props. (It may be simpler to achieve this other ways, for example by using recompose's mapProps.)You may wish to override
areStatePropsEqualto usestrictEqualif yourmapStateToPropsuses 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 timemapStateToPropsis called.You may wish to override
areMergedPropsEqualto implement adeepEqualif your selectors produce complex props. ex: nested objects, new arrays, etc. (The deep equal check should be faster than just re-rendering.)
返回值
根據(jù)配置信息,返回一個注入了 state 和 action creator 的 React高階組件。這個組件是由connectAdvanced創(chuàng)建的。
例子
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
TodoAppwill 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):