React-Redux
是Redux
的官方React
綁定庫。它可以使你的React
組件從Redux store
中讀取數據,而且向store
分發actions
以更新數據javascript
在你的React app中使用React-Redux:html
npm install --save react-redux
或者java
yarn add react-redux
Provider
和connect
React-Redux 提供<Provider/>
組件,可以使你的整個app訪問到Redux store
中的數據:react
import React from "react"; import ReactDOM from "react-dom"; import { Provider } from "react-redux"; import store from "./store"; import App from "./App"; const rootElement = document.getElementById("root"); ReactDOM.render( <Provider store={store}> <App /> </Provider>, rootElement );
React-Redux
提供一個connect
方法可以讓你把組件和store
鏈接起來。git
一般你能夠如下面這種方式調用connect
方法:github
import { connect } from "react-redux"; import { increment, decrement, reset } from "./actionCreators"; // const Counter = ... const mapStateToProps = (state /*, ownProps*/) => { return { counter: state.counter }; }; const mapDispatchToProps = { increment, decrement, reset }; export default connect( mapStateToProps, mapDispatchToProps )(Counter);
爲了進一步瞭解如何實際使用React-Redux
,咱們將一步一步建立一個todo list
appweb
跳轉到:npm
React UI 組件redux
咱們所用到的React UI
組件以下:api
TodoApp
:咱們應用的入口組件,它render
出AddTodo
,TodoList
和VisibilityFilters
組件AddTodo
:容許用戶在點擊Add Todo
按鈕後,向todo list中加入一個新的待辦項:
input
監聽onChange
事件以設置state
Add Todo
按鈕後,該組件dispatch
一個action
,向store
中添加一個新的待辦項。(這個action
是咱們由React-Redux
提供的)TodoList
:渲染出待辦項列表的組件:
VisibilityFilter
被選擇後,可以渲染出所匹配的待辦項列表Todo
:僅負責渲染單個todo
待辦項:
onClick
事件後,dispatch
一個能切換完成狀態的action
VisibilityFilters
:渲染一個filters
集合:_all_,_complete_ 以及 _incomplete_。單擊每一項可以篩選匹配的todos
:
activeFilter
屬性以表示當前用戶選擇的過濾條件。選中的filter會顯示出下劃線。dispatch
名爲setFilter
的action
以更新已選過濾條件constants
:保存咱們的app全部須要的常量數據index
將app渲染到DOM中the Redux Store
應用的Redux
部分遵循Redux
官方文檔建議模式進行搭建:
Store:
todos
:標準化的todos的reducer
。包含了byIds
的待辦項map
對象結構,和一個包含了全部待辦項id的allIds
數組visibilityFilters
:簡單的字符串all
, completed
, or incomplete
.Action Creators:
addTodo
:建立增添待辦項的action
。接收一個string
變量content
,返回ADD_TODO
類型的action,以及一個payload
對象(包含了自增的id
和content
屬性)toggleTodo
:建立一個切換待辦項的action
。只接收一個number
類型的變量id
,返回TOGGLE_TODO
類型action以及僅含id
屬性的payload
對象。setFilter
:建立設置app當前過濾條件的action
。接收一個string
類型變量filter
返回一個SET_FILTER
類型action一集一個包含filter
自身的payload
對象。Reducers
todos
reducer:
- 在接收到`ADD_TODO` action時,將`id`追加到`allIds`數組,而且更新`byIds` - 在接收到`TOGGLE_TODO` action時,切換`completed`狀態
VisibilityFilters reducer
:在接收到SET_FILTER
action 時負責更新VISIBILITY_FILTERS
狀態Action Types
actionTypes.js
文件來保存全部的action types
常量,以便複用Selectores
getTodoList
:從todos
store中返回allIds
列表getTodoById
:經過id
查詢store中的todo項getTodos
:有點複雜。它接收allIds
數組中的全部id
,找到每個對應的byIds
中的todo
,返回最終的todos
數組getTodosByVisibilityFilter
:根據篩選條件過濾todos你能夠查看上面這些UI組件的和還沒有鏈接的Redux Store
源碼
下面咱們將展現如何使用React-Redux
將store
鏈接到咱們的app中
首先咱們須要讓store
成爲咱們app中可訪問的對象。爲此,咱們將用React-Redux
提供給咱們的<Provider/>
組件包裹咱們的根組件
// index.js import React from "react"; import ReactDOM from "react-dom"; import TodoApp from "./TodoApp"; import { Provider } from "react-redux"; import store from "./redux/store"; const rootElement = document.getElementById("root"); ReactDOM.render( <Provider store={store}> <TodoApp /> </Provider>, rootElement );
要注意到store
是以一個prop
由<Provider/>
傳遞到被包裹的<TodoApp/>
中的
React-Redux
提供一個connect
方法使你能夠從Redux store
中讀取數據(以及當store更新後,從新讀取數據)
connect
方法接收兩個參數,都是可選參數:
mapStateToProps
:每當store state
發生變化時,就被調用。接收整個store state
,而且返回一個該組件所須要的數據對象mapDispatchToProps
:這個參數能夠是一個函數或對象
dispatch
做爲一個參數,而且返回一個可以使用dispatch
來分發actions的若干函數組成的對象prop function
並會在調用時自動分發actions。注意: 咱們建議使用這種形式。一般,你能夠這樣去connect
:
const mapStateToProps = (state, ownProps) => ({ // ... 從state中處理的一些數據,以及可選的ownProps }); const mapDispatchToProps = { // ... 一般是action creators構成的對象 }; // `connect`返回一個新的函數,能夠接收一個待包裝的組件 const connectToStore = connect( mapStateToProps, mapDispatchToProps ); // 上面的函數可以返回一個已經包裝、鏈接過的組件 const ConnectedComponent = connectToStore(Component); // 咱們一般寫成一條語句以下: connect( mapStateToProps, mapDispatchToProps )(Component);
下面讓咱們開始編寫<AddTodo/>
。它要可以觸發store
的變化從而增長新的todos
。所以,他要可以向store dispatch
actions。下面就是具體流程。
咱們的addTodo
action建立函數以下所示:
// redux/actions.js import { ADD_TODO } from "./actionTypes"; let nextTodoId = 0; export const addTodo = content => ({ type: ADD_TODO, payload: { id: ++nextTodoId, content } }); // ... other actions
把它傳遞到connect
,咱們的組件就可以以一個prop
接收到它。而且一旦咱們調用,它就可以自動的分發actions
。
// components/AddTodo.js // ... other imports import { connect } from "react-redux"; import { addTodo } from "../redux/actions"; class AddTodo extends React.Component { // ... component implementation } export default connect( null, { addTodo } )(AddTodo);
注意到如今<AddTodo/>
已經被一個父組件<Connect(AddTodo)/>
所包裝。同時,<AddTodo/>
如今擁有了一個prop
:addTodo
action
咱們也須要實現handleAddTodo
方法以便分發addTodo
action 而且重置input
框
// components/AddTodo.js import React from "react"; import { connect } from "react-redux"; import { addTodo } from "../redux/actions"; class AddTodo extends React.Component { // ... handleAddTodo = () => { // 分發action以增長todo項 this.props.addTodo(this.state.input); // 將state的input置爲空字符串 this.setState({ input: "" }); }; render() { return ( <div> <input onChange={e => this.updateInput(e.target.value)} value={this.state.input} /> <button className="add-todo" onClick={this.handleAddTodo}> Add Todo </button> </div> ); } } export default connect( null, { addTodo } )(AddTodo);
如今咱們的<AddTodo/>
已經鏈接到了store
。當咱們增長一個新的todo
時,該組件就可以分發action
從而改變store
。咱們如今還不能看到這個效果,由於別的組件還沒有鏈接到store
。若是你安裝了Redux DevTools
谷歌瀏覽器擴展程序,那麼你能夠看到action
已經被分發了:
你也可以看到store
相應地發生了變化。
<TodoList/>
組件負責渲染todos
列表。所以,他須要從store
中讀取數據。咱們經過調用connect
方法,並向其中傳入mapStateToProps
參數從而提供給組件所須要的部分來自store
數據。
咱們的<Todo/>
組件接收一個todo
項做爲prop
。咱們從todos
的btIds
對象獲取到所需信息。然而,咱們也須要store
中的allIds
字段的信息,以便指明哪些todos以哪一種順序渲染。咱們的mapStateToProps
方法可能長這個樣子:
// components/TodoList.js // ...other imports import { connect } from "react-redux"; const TodoList = // ... UI component implementation const mapStateToProps = state => { const { byIds, allIds } = state.todos || {}; const todos = allIds && allIds.length ? allIds.map(id => (byIds ? { ...byIds[id], id } : null)) : null; return { todos }; }; export default connect(mapStateToProps)(TodoList);
幸虧咱們有一個selector
專門作這個事情,咱們只須要簡單地導入selector
而且使用它。
// redux/selectors.js export const getTodosState = store => store.todos; export const getTodoList = store => getTodosState(store) ? getTodosState(store).allIds : []; export const getTodoById = (store, id) => getTodosState(store) ? { ...getTodosState(store).byIds[id], id } : {}; export const getTodos = store => getTodoList(store).map(id => getTodoById(store, id));
// components/TodoList.js // ...other imports import { connect } from "react-redux"; import { getTodos } from "../redux/selectors"; const TodoList = // ... UI component implementation export default connect(state => ({ todos: getTodos(state) }))(TodoList);
咱們建議將全部複雜的查找和計算數據的方法封裝到selector
中。此外,你從此能夠經過使用Reselect編寫「memoized」 selectors來跳過沒必要要的工做從而優化性能。(查閱Redux中文文檔 | 計算衍生數據以及博客:Idiomatic Redux: Using Reselect Selectors for Encapsulation and Performance以得到更多的關於爲什麼以及如何使用selector
方法的信息)
既然咱們的<TodoList/>
也已經鏈接到了store
。它應該可以接收到todos
列表了,遍歷他們,而後把每個傳遞給<Todo/>
組件。<Todo/>
組件會將它們渲染到瀏覽器中,如今嘗試增長一個todo
待辦項。它應該能當即出如今咱們的todo
列表中!
咱們接下來會connect
更多的組件。在開始以前,讓咱們先暫停一下,首先學習更多關於connect
的知識。
connect
的方式有不一樣的方式來調用connect
,這取決於你如今編寫的組件類型。現對經常使用的方式進行總結:
不訂閱Store | 訂閱Store | |
---|---|---|
不注入Action Creators | connect()(Component) |
connect(mapStateToProps)(Component) |
注入Action Creators | connect(null, mapDispatchToProps)(Component) |
connect(mapStateToProps, mapDispatchToProps)(Component) |
不訂閱store而且不注入action建立函數
若是你調用connect
方法而且不傳入任何參數,那麼你的組件將會:
store
改變時不可以從新渲染props.dispatch
方法以便你手動分發actions// ... Component export default connect()(Component); // 組件將接收 `dispatch` (正如 <TodoList />!)
訂閱store但不注入action建立函數
若是你調用connect
方法而且只傳入了mapStateToProps
方法,你的組件將會:
mapStateToProps
從store中提取的部分值,當這些值改變時會從新渲染props.dispatch
以便你手動分發actions// ... Component const mapStateToProps = state => state.partOfState; export default connect(mapStateToProps)(Component);
不訂閱store但注入action建立函數
若是你調用connect
方法並只傳入mapDispatchToProps
參數,你的組件將會:
props
形式接收每一個你經過mapDispatchToProps
注入的action建立函數,可以在你調用後自動分發actionsimport { addTodo } from "./actionCreators"; // ... Component export default connect( null, { addTodo } )(Component);
訂閱store而且注入action建立函數
若是你在connect
方法中傳入了mapStateToProps
和mapDispatchToProps
,你的組件將會:
mapStateToProps
從store中提取的部分值,當這些值改變時會從新渲染props
形式接收每一個你經過mapDispatchToProps
注入的action建立函數,可以在你調用後自動分發actionsimport * as actionCreators from "./actionCreators"; // ... Component const mapStateToProps = state => state.partOfState; export default connect( mapStateToProps, actionCreators )(Component);
上面這四個例子基本覆蓋了全部connect
的用法。若是想了解更多關於connnect
的信息,能夠繼續閱讀[API 部分]()內容
如今咱們把<TodoApp/>
餘下的部分鏈接到store
咱們該如何實現切換todos
的操做呢?一個聰明的讀者也許已經有答案了。若是你截止目前的全部步驟都是緊跟指導完成的,如今是一個拋開指南、本身實現該功能的絕佳時機。不出所料,咱們以相似的方法鏈接<Todo/>
以分發toggleTodo
:
// components/Todo.js // ... other imports import { connect } from "react-redux"; import { toggleTodo } from "../redux/actions"; const Todo = // ... 實現組件 export default connect( null, { toggleTodo } )(Todo);
如今咱們的todo
可以切換爲complete
狀態了。立刻就行了!
終於,讓咱們開始實現VisibilityFilters
功能。
<VisiblityFilterse/>
組件須要從store中讀取當前選中的過濾條件,而且分發actions
。所以,咱們須要把mapStateToProps
以及mapDispatchToProps
都傳遞給connect
方法。mapStateToProps
可以做爲visiblityFilter
狀態的一個簡單的訪問器。mapDispatchToProps
會包括setFilter
action建立函數。
// components/VisibilityFilters.js // ... other imports import { connect } from "react-redux"; import { setFilter } from "../redux/actions"; const VisibilityFilters = // ... 組件實現 const mapStateToProps = state => { return { activeFilter: state.visibilityFilter }; }; export default connect( mapStateToProps, { setFilter } )(VisibilityFilters);
同時,咱們也要更新咱們的<TodoList/>
組件來根據篩選條件過濾todos
。先前咱們傳遞給<TodoList/>
connect 方法的mapStateToProps
正如一個簡單的選擇了全部列表中的todos的selector。如今咱們來寫一個selector以經過todos的狀態來進行篩選。
// redux/selectors.js // ... other selectors export const getTodosByVisibilityFilter = (store, visibilityFilter) => { const allTodos = getTodos(store); switch (visibilityFilter) { case VISIBILITY_FILTERS.COMPLETED: return allTodos.filter(todo => todo.completed); case VISIBILITY_FILTERS.INCOMPLETE: return allTodos.filter(todo => !todo.completed); case VISIBILITY_FILTERS.ALL: default: return allTodos; } };
而後藉助selector鏈接到store
// components/TodoList.js // ... const mapStateToProps = state => { const { visibilityFilter } = state; const todos = getTodosByVisibilityFilter(state, visibilityFilter); return { todos }; }; export default connect(mapStateToProps)(TodoList);
如今咱們已經用React-Redux
完成了一個很簡單的todo app
案例。咱們全部的組件都已經鏈接到了store。是否是很棒呢?🎉🎊
mapStateToProps
抽取數據做爲傳遞給connect
的第一個參數,mapStateToProps
用來從store中選擇被鏈接的組件所須要的部分數據。常以mapState
縮寫來表示。
mapStateToProps
mapStateToProps
應該聲明爲一個方法:
function mapStateToProps(state, ownProps?)
他接收的第一個參數是state
,可選的第二個參數時ownProps
,而後返回一個被鏈接組件所須要的數據的純對象。
這個方法應做爲第一個參數傳遞給connect
,而且會在每次Redux store state
改變時被調用。若是你不但願訂閱store
,那麼請傳遞null
或者undefined
替代mapStateToProps
做爲connect
的第一個參數。
不管mapStateToProps
是使用function
關鍵字聲明的(function mapState(state) { } )
仍是以一個箭頭函數(const mapState = (state) => { } )
的形式定義的——它都可以以一樣的方式生效。
state
ownProps
(可選)state
mapStateToProps
的第一個參數是整個Redux store state對象(與store.getState()
方法返回的值相同)。所以第一個參數一般命名爲state
(固然你也能夠選擇別的名字,可是叫store
就不推薦了——由於它是state
值而不是store
實例)
mapStateToProps
方法至少要傳遞state
參數。
// TodoList.js function mapStateToProps(state) { const { todos } = state; return { todoList: todos.allIds }; }; export default connect(mapStateToProps)(TodoList);
ownProps
(可選)
若是你的組件須要用自身的props
數據以從store中檢索出數據,你能夠傳入第二個參數,ownProps
。這個參數將包含全部傳遞給由connect
生成的包裝組件的props
。
// Todo.js function mapStateToProps(state, ownProps) { const { visibilityFilter } = state; const { id } = ownProps; const todo = getTodoById(state, id); // 組件額外接收: return { todo, visibilityFilter }; }; // 以後,在你的應用裏,渲染一個以下父組件: <ConnectedTodo id={123} /> // 你的組件接收 props.id, props.todo, 以及 props.visibilityFilter
你不須要把ownProps
中的值再添加入mapStateToProps
返回的對象中,connect
會自動的把這些不一樣源的prop
合併爲一個最終的prop
集。
你的mapStateToProps
方法應該返回一個包含了組件用到的數據的純對象:
prop
例如:
function mapStateToProps(state) { return { a : 42, todos : state.todos, filter : state.visibilityFilter } } // 組件會接收: props.a, props.todos,以及 props.filter
注意:在一些高級場景中,你可能須要更多地對於渲染性能的控制,mapStateToProps
也能夠返回一個方法。在這種狀況下,那個所返回的方法會作爲一個特定組件實例的最終的mapStateToProps
。這樣一來,你就能夠對每一個實例進行memoization
。參考[高級用法]()部分以獲取更多信息。也能夠看 PR #279以及上面增長的測試。但 大部分應用根本不須要這樣作
mapStateToProps
改造從store中取出的數據mapStateToProps
方法可以,且應該,作更多的事情,而不只僅是返回一個state.someSlice
。他們有責任去改造組建所須要的store中的數據。好比說,返回一個特定prop
名稱的值,從state樹中不一樣部分取出數據片斷併合併爲一個總體,以及以不一樣的方式轉化store。
咱們強烈建議使用selector
方法去封裝抽取state樹中的特定位置的值。Memoized selector
方法也在提升應用性能上起到了關鍵做用。(參考本頁如下部分:[高級用法:性能]()以獲取更多關於爲什麼以及如何使用selectors的細節)
mapStateToProps
方法應該足夠快一旦store改變了,全部被鏈接組件中的全部的mapStateToProps
方法都會運行。所以,你的mapStateToProps
方法必定要足夠快。這也意味着緩慢的mapStateToProps
方法會成爲你應用的一個潛在瓶頸。
做爲「重塑數據」想法的一部分,mapStateToProps
方法常常須要以各類方式來轉化數據(好比過濾數組,遍歷IDs數組映射到對應的對象,或者從Immutable.js對象中抽取純js值)。這些轉化的開銷一般很高昂,不只表如今運行轉化操做的開銷上,也表如今判斷最終是否要更新組件上。若是的確須要考慮性能問題了,那麼要確保你的這些轉化只發生在所輸入的值發生變化的時候。
mapStateToProps
方法應該純淨且同步正如Redux Reducer,一個mapStateToProps
方法應該是100%純淨的而且是同步的。他應該僅接受state
(以及ownProps
)做爲參數,而後以props
形式返回組件所須要的數據。他不應該觸發任何異步操做,如AJAX請求數據,方法也不能聲明爲async
形式。
mapStateToProps
和性能React-Redux 內部實現了shouldComponentUpdate
方法以便在組件用到的數據發生變化後可以精確地從新渲染。默認地,React-Redux使用「===
」對mapStateToProps
返回的對象的每個字段逐一對比,以判斷內容是否發生了改變。但凡是有一個字段改變了,你的組件就會被從新渲染以便接收更新過的props
值。注意到,返回一個相同引用的突變對象(mutated object)是一個常見錯誤,由於這會致使你的組件不能如期從新渲染。
總結一下傳入mapStateToProps
參數來抽取store中的數據的connect
方法包裝過的組件行爲:
<span/> | state=>stateProps |
(state,ownProps)=>stateProps |
---|---|---|
mapStateToProps 運行條件: |
store state 發生改變 |
store state 發生改變或 任何 ownProps 字段改變 |
組件從新渲染條件 | 任何stateProps 字段改變 |
任何stateProps 字段改變或 任何 ownProps 字段改變 |
React-Redux進行淺比較來檢查mapStateToProps
的結果是否改變了。返回一個新對象或數組引用十分容易操做,但會形成你的組件在數據沒變的狀況下也從新渲染。
不少常見的操做都會返回新對象或數組引用:
someArray.map()
或者someArray.filter()
array.concat
array.slice
Object.assgin
{...oldState,...newData}
把這些操做放在memeoized selector functions
中確保它們只在輸入值變化後運行。這樣也可以確保若是輸入值沒有改變,mapStateToProps
仍然返回與以前的相同值,而後connect
就可以跳太重渲染過程。
轉化數據常常開銷很大(而且一般會建立一個新的對象引用)。爲了使你的mapStateToProps
方法足夠快,你應該僅在相關數據改變時從新運行這些複雜的轉化。
有下面幾種形式來達到這樣的目的:
render()
生命週期中完成mapStateToProps
方法中完成,那麼咱們建議使用memoized selector functions以確保轉化僅發生在輸入的數據變化時。考慮Immutable.js性能
Immutable.js 的做者Lee Byron在Twitter中明確建議了若是開始考慮性能了要避免使用toJS
Perf tip for #immutablejs: avoid .toJS() .toObject() and .toArray() all slow full-copy operations which render structural sharing useless.
還有一些別的關於Immutable.js的性能提高建議——參見下方的連接列表。
mapStateToProps
在store state相同的狀況下不會運行connect
生成的包裝組件會訂閱Redux store。每當action被分發後,它就調用store.getState()
並檢查是否lastState===currentState
。若是兩個狀態值引用徹底相同,那麼mapStateToProps
就不會運行,由於組件假設了你餘下的store state也沒有發生改變。
Redux的combineReducers
功能函數會嘗試對其優化。若是全部reducer都沒有返回新值,那麼combineReducers
會返回舊state對象而不是新的。這意味着,一個reducer中的突變不會使根state對象更新,固然UI也不會從新渲染。
當僅有(state
)時,每當根store state對象不一樣了,函數就會運行。
當有(state
,ownProps
)兩個參數時,每當store state不一樣、或每當包裝props
變化時,函數都會運行。
這意味着你不該該增長ownProps
參數,除非你實在是須要它,不然你的mapStateToProps
函數會比它實際須要運行次數運行更屢次。
關於這個行爲有一些極端案例。arguments的數量決定了mapStateToProps
是否接收ownProps
參數。
若是先前定義函數的時候包含了一個命令參數,mapStateToProps
就不會接收ownProps
function mapStateToProps(state) { console.log(state); // state console.log(arguments[1]); // undefined } const mapStateToProps = (state, ownProps = {}) => { console.log(state); // state console.log(ownProps); // undefined }
若是以前定義的函數包含了0個或2個命令參數,他就須要接收ownProps
參數:
function mapStateToProps(state, ownProps) { console.log(state); // state console.log(ownProps); // ownProps } function mapStateToProps() { console.log(arguments[0]); // state console.log(arguments[1]); // ownProps } function mapStateToProps(...args) { console.log(args[0]); // state console.log(args[1]); // ownProps }
教程
性能
toJS
, toArray
and toObject
for PerformanceQ&A
Connect
: 使用mapDispatchToProps
分發actions做爲第二個傳入connect
的參數,mapDispatchToProps
能夠實現向store中分發acions。
dispatch
是Redux store實例的一個方法,你會經過store.dispatch
來分發一個action。這也是惟一觸發一個state變化的途徑。
使用React-Redux後,你的組件就再也不須要直接和store打交道了——connect
會爲你完成這件任務,React-Redux提供了兩種能夠分發actions的方式:
props.dispatch
而後本身分發actionsconnect
可以接收一個mapDispatchToProps
做爲第二個參數,這將讓你可以建立dispatch調用方法,而後把這些方法做爲props傳遞給你的組件。dispatch
若是你不爲connect()
指明第二個參數,你的組件會默認接收dispatch
。好比:
connect()(MyComponent); // 與下面語句等價 connect( null, null )(MyComponent); // 或者 connect(mapStateToProps /** 沒有第二個參數 */)(MyComponent);
一旦你以這種方式鏈接了你的組件,你的組件就會接收props.dispatch
。你能夠用它來向store中分發actions。
function Counter({ count, dispatch }) { return ( <div> <button onClick={() => dispatch({ type: "DECREMENT" })}>-</button> <span>{count}</span> <button onClick={() => dispatch({ type: "INCREMENT" })}>+</button> <button onClick={() => dispatch({ type: "RESET" })}>reset</button> </div> ); }
mapDispatchToProps
參數提供一個mapDispatchToProps
參數可以讓你指明你的組件所實際須要分發的那些actions。它容許你提供action分發函數做爲props,這樣一來,你不用再進行props.dispatch(() => increment())
調用,你能夠直接props.increment()
。你這麼作是出於下面幾個緣由:
更加聲明式的
首先,把分發邏輯封裝到函數中使得整個實現更加聲明式。分發一個action而後讓Redux store處理數據流,表現出了你如何實現這一行爲而不只僅是隻關心它作了什麼。
單擊按鈕後分發一個action也許是個不錯的例子。把一個button直接鏈接從概念上來說有點說不通,button也沒有dispatch
引用。
// button須要有意識地 "dispatch" <button onClick={() => dispatch({ type: "SOMETHING" })} /> // button看起來不像在 "dispatch", <button onClick={doSomething} />
一旦你把全部的action creators使用函數封裝起來以後,你的組件就不須要再dispatch
了。所以,若是你定義了mapDispatchToProps
被鏈接組件就再也不接收到dispatch
了。
把action分發邏輯向子(未鏈接)組件傳遞
此外,你如今也可以向下傳遞你的action分發函數給子組件(可能還沒有鏈接)。這樣就可以使更多的組件分發actions
,且它們對Redux也是無感知的。
// 把toggleTodo 傳遞給子組件 // 讓Todo 能分發 toggleTodo action const TodoList = ({ todos, toggleTodo }) => ( <div> {todos.map(todo => ( <Todo todo={todo} onClick={toggleTodo} /> ))} </div> );
這就是React-Redux的connect
所作的工做——封裝與Redux Store對話的邏輯,而且你再也不須要操心。你也應該在你的實現中充分利用這一點。
mapDispatchToProps
的形式mapDispatchToProps
參數有兩種形式:函數形式自定義化程度更高,對象形式更簡單。
dispatch
和可選擇的ownProps
注意:咱們建議使用對象形式的
mapDispatchToProps
,除非你須要以某種自定義形式進行分發操做
mapDispatchToProps
定義爲一個函數將mapDispatchToProps
定義爲一個函數使你更靈活地定義你的組件可以接收到的函數、以及這些函數如何分發actions。你對dispatch
和ownProps
都具備訪問權限。你能夠藉此機會編寫你的鏈接組件的自定義函數。
dispatch
ownProps
(可選)dispatch
mapDispatchToProps
函數調用時以dispatch
做爲第一個參數。一般狀況下,你會利用這個參數來返回一個內部調用了dispatch()
的新函數,而後內部傳遞一個純的action對象或者action建立函數的返回值。
const mapDispatchToProps = dispatch => { return { // 分發純action對象 increment: () => dispatch({ type: "INCREMENT" }), decrement: () => dispatch({ type: "DECREMENT" }), reset: () => dispatch({ type: "RESET" }) }; };
你也可能須要把一些參數轉發給你的action建立函數
const mapDispatchToProps = dispatch => { return { // 直接轉發參數 onClick: event => dispatch(trackClick(event)), // 間接轉發參數 onReceiveImpressions: (...impressions) => dispatch(trackImpressions(impressions)) }; };
ownProps
(可選)
你的mapDispatchToProps
函數是能夠接收兩個參數的,第一個是dispatch
,傳遞給鏈接組件的props
即爲mapDispatchToProps
的第二個參數,而後在組件接收到新的props
後會從新調用。
這意味着,你應該在組件props
改變階段從新把新的props
綁定到action分發函數中去,而不是在組件從新渲染階段進行。
// 在組件re-rendering階段綁定 <button onClick={() => this.props.toggleTodo(this.props.todoId)} />; // 在 props 改變階段綁定 const mapDispatchToProps = (dispatch, ownProps) => { toggleTodo: () => dispatch(toggleTodo(ownProps.todoId)); };
你的mapDispatchToProps
函數應該的返回一個純對象。
prop
,而且字段的值一般是一個調用後能分發action的函數。dispatch()
中使用了action建立函數(區別於純對象形式的action),一般約定字段名與action建立函數的名稱相同const increment = () => ({ type: "INCREMENT" }); const decrement = () => ({ type: "DECREMENT" }); const reset = () => ({ type: "RESET" }); const mapDispatchToProps = dispatch => { return { // 分發由action creators建立的actions increment: () => dispatch(increment()), decrement: () => dispatch(decrement()), reset: () => dispatch(reset()) }; };
mapDispatchToProps
的函數返回值會合併到你的組件props
中去。你就可以直接調用它們來分發action。
function Counter({ count, increment, decrement, reset }) { return ( <div> <button onClick={decrement}>-</button> <span>{count}</span> <button onClick={increment}>+</button> <button onClick={reset}>reset</button> </div> ); }
(Counter案例的完整代碼參見:CodeSandbox)
bindActionCreators
定義mapDispatchToProps
函數手動封裝這些函數實在是繁瑣,因此Redux提供了一個函數簡化這個操做。
bindActionCreators
將值爲action creators
的對象,轉化爲同鍵名的新對象,但將每一個action creators封裝到一個dispatch調用中,以即可以直接調用它們。參閱 Redux | bindActionCreators
bindActionCreators
接收兩個參數:
由bindActionCreators
生成的包裝函數會自動轉發它們全部的參數,因此你不須要在手動操做了。
import { bindActionCreators } from "redux"; const increment = () => ({ type: "INCREMENT" }); const decrement = () => ({ type: "DECREMENT" }); const reset = () => ({ type: "RESET" }); // 綁定一個action creator // 返回 (...args) => dispatch(increment(...args)) const boundIncrement = bindActionCreators(increment, dispatch); // 綁定一個action creators構成的object const boundActionCreators = bindActionCreators({ increment, decrement, reset }, dispatch); // 返回值: // { // increment: (...args) => dispatch(increment(...args)), // decrement: (...args) => dispatch(decrement(...args)), // reset: (...args) => dispatch(reset(...args)), // }
在mapDispatchToProps
中使用bindActionCreators
函數:
import { bindActionCreators } from "redux"; // ... function mapDispatchToProps(dispatch) { return bindActionCreators({ increment, decrement, reset }, dispatch); } // 組件能接收到 props.increment, props.decrement, props.reset connect( null, mapDispatchToProps )(Counter);
dispatch
若是提供了mapDispatchToProps
,組件將再也不接收到默認的dispatch
。但你能夠經過在mapDispatchToProps
的return
中添加dispatch
把它從新注入你的組件。多數狀況下,你不須要這麼作。
import { bindActionCreators } from "redux"; // ... function mapDispatchToProps(dispatch) { return { dispatch, ...bindActionCreators({ increment, decrement, reset }, dispatch) }; }
mapDispatchToProps
定義爲一個對象你已經注意到了,在React組件中分發Redux actions的過程都十分相似:定義action建立函數,把它包裝在形如(…args) => dispatch(actionCreator(…args))
的另外一個函數,而後把那個包裝函數做爲props
傳遞給你的組件。
由於這一流程實在是太通用了,connect
支持了一個「對象簡寫」形式的mapDispatchToProps
參數:若是你傳遞了一個由action creators構成的對象,而不是函數,connect
會在內部自動爲你調用bindActionCreators
咱們建議適中使用這種「對象簡寫」形式的mapDispatchToProps
,除非你有特殊理由須要自定義dispatching
行爲。
注意到:
mapDispatchToProps
對象的字段都被假設爲一個action建立函數dispatch
做爲一個prop
// React-Redux 自動爲你作: dispatch => bindActionCreators(mapDispatchToProps, dispatch);
所以,咱們的mapDispatchToProps
能夠簡寫爲:
const mapDispatchToProps = { increment, decrement, reset };
既然變量名取決於你,你可能想把它命名爲actionCreators
或者甚至直接在調用connect
時使用一個行內對象:
import {increment, decrement, reset} from "./counterActions"; const actionCreators = { increment, decrement, reset } export default connect(mapState, actionCreators)(Counter); // 或者 export default connect( mapState, { increment, decrement, reset } )(Counter);
dispatch
?也就是說會報錯:
TypeError: this.props.dispatch is not a function
在你試圖調用this.props.dispatch
時這個錯誤就很常見了,由於實際上dispatch
並無注入到你的組件。
dispatch
僅在下面這些狀況下注入組件:
mapDispatchToProps
默認的mapDispatchToProps
只是簡單的dispatch => ({ dispatch })
。若是你不提供mapDispatchToProps
,dispatch
會以上面的形式自動提供給你。
換言之,也就是你這麼作了:
//組件接收 `dispatch` connect(mapStateToProps /** 沒有第二參數*/)(Component);
mapDispatchToProps
明確地返回了dispatch
你也許想把dispatch
帶回你的組件,經過形以下面的定義方法:
const mapDispatchToProps = dispatch => { return { increment: () => dispatch(increment()), decrement: () => dispatch(decrement()), reset: () => dispatch(reset()), dispatch }; };
或者,使用bindActionCreators
:
import { bindActionCreators } from "redux"; function mapDispatchToProps(dispatch) { return { dispatch, ...bindActionCreators({ increment, decrement, reset }, dispatch) }; }
本錯誤可參考:Redux’s GitHub issue #255.
有關是否須要對組件提供dispatch
的討論(Dan Abramov對#255的回覆)。您能夠閱讀它們以進一步瞭解目前這麼作的目的。
mapStateToProps
而僅使用mapDispatchToProps
?固然。你能夠經過給第一個參數傳入null
或undefined
來跳過它。你的組件就不會訂閱store但仍然可以接收到mapDispatchToProps
定義的dispatch props
connect( null, mapDispatchToProps )(MyComponent);
store.dispatch
嗎?不管是直接import
的仍是從context
拿到的store
,這都不是一種與store交互的良好模式(參見Redux FAQ entry on store setup以獲取更多詳情)。讓React-Redux的connect
來獲取對store的訪問權,而且使用dispatch
分發actions。
教程
相關文檔
Q&A
this.props
using connect with Redux?this.props.dispatch
is undefined
if using mapDispatchToProps
store.dispatch
, call this.props.dispatch
injected by connect
insteadmapDispatchToProps
without mapStateToProps
in Redux?使Redux store可用於connect()
調用下面組件層次結構中。一般,若是沒有<Provider>
包裝父級或祖先組件,則沒法使用connect()
。
若是你 實在 是須要,你能夠手動地將store
做爲一個prop
傳遞給被鏈接組件,可是咱們僅建議在單元測試中對store
僞造(stub),或者在非徹底React代碼庫中這麼作。一般,你就使用<Provider>
吧。
store
(Redux Store):你應用裏的惟一Redux storechildren
(ReactElement):根組件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
是advancedConnect
的對外提供的一個較爲經常使用的API,可以用於大多數場景。
它不會改變傳遞給它的組件類,而是返回了一個新的、被鏈接的組件供你使用。
mapStateToProps(state, [ownProps]): stateProps
] (Function):若是指定過了這個參數,那麼新組件就會向Redux store訂閱更新。這意味着任什麼時候候store一旦更新,mapStateToProps
就會被調用。mapStateToProps
的結果必須是一個純對象,以後該對象會合併到組件的props
若是你的mapStateToProps
函數被聲明爲接收兩個參數,那麼第一個參數就是store state,第二個參數就是傳遞給鏈接組件的props
。mapStateToProps
函數也會在鏈接組件的props
經過淺比較發現改變時從新運行。(第二個參數約定命名爲ownProps
)。
注意:在一些高級場景中,你可能須要更多地對於渲染性能的控制,mapStateToProps
也能夠返回一個方法。在這種狀況下,那個所返回的方法會作爲一個特定組件實例的最終的mapStateToProps
。這樣一來,你就能夠對每一個實例進行memoization
。參考[高級用法]()部分以獲取更多信息。也能夠看 PR #279以及上面增長的測試。但 大部分應用根本不須要這樣作
mapStateToPros
函數的第一個參數是整個Redux Store state,而後該函數返回一個能夠傳遞給鏈接組件的props
對象,這個常被稱爲selector
。使用reselect來高效地構建selectors以及計算衍生數據
mapDispatchToProps(dispatch, [ownProps]): dispatchProps
] (Object or Function):若是傳遞的是一個對象,其內部的每個函數都被假定爲一個Redux action 建立函數。這個對象會做爲props
傳遞給鏈接組件,且其內部每一個字段名都與action creators相同,但被一個能調用dispatch
方法的函數包裝,這樣一來這些分發函數就能夠直接調用。
若是傳遞的是一個函數,其第一個參數爲dispatch
。你想如何使用dispatch
去綁定action建立函數均可以。(貼士:你可使用Redux提供的bindActionCreators()
方法)
若是你的mapDispatchToProps
方法被聲明爲接收兩個變量,那麼其第一個參數是dispatch
,第二個參數就是傳遞給鏈接組件的props
。mapDispatchToProps
函數也會在鏈接組件的props
經過淺比較發現改變時從新運行。(第二個參數約定命名爲ownProps
)。
若是你不提供mapDispatchToProps
函數(或action cretors對象)默認的這一行爲實現僅是經過向你的組件props
注入dispatch
注意:在一些高級場景中,你可能須要更多地對於渲染性能的控制,mapDispatchToProps
也能夠返回一個方法。在這種狀況下,那個所返回的方法會作爲一個特定組件實例的最終的mapDispatchToProps
。這樣一來,你就能夠對每一個實例進行memoization
。能夠看 PR #279以及上面增長的測試。但 大部分應用根本不須要這樣作
mergeProps(stateProps,dispatchProps,ownProps):props
]:(Function):若是指明瞭這個參數,它內部要接收mapStateToProps()
,mapDispatchToProps()
以及父props
三個參數。這個函數參數所返回的純對象將會做爲props
傳遞給被包裝組件。你可使用這個函數來根據props
選擇一部分state
,或者把action建立函數綁定在一個特定的props
變量上。若是你省略這個參數,則會默認使用Object.assign({}, ownProps, stateProps, dispatchProps)
方法。
[options
](Object):這個參數能夠進一步定製connector
的行爲。除了能夠傳遞給connectAdvanced()
的選項(見下),connect()
還接受如下額外選項:
pure
] (Boolean):若爲true
,在相關的state/props
分別進行了淺比較後發現沒有改變的狀況下,connect()
會避免從新渲染以及對mapStateToProps
、mapDispatchToProps
和mergeProps
的調用。假設咱們的被包裝組件是一個不依賴任何input
或state
的「純」組件,僅依賴於props
和被選中的Redux store state
除外。默認值:true
。areStatesEqual
](Function):當pure
爲true
,比較下一個store state
和其先前值。默認值:嚴格相等(===)
areOwnPropsEqual
](Function):當pure
爲true
,比較下一個`props`和其先前值。默認值:`淺比較` - [`areStatePropsEqual`](Function):當`pure`爲`true`,比較`mapStateToProps `的結果和其先前值。默認值:`淺比較` - [`areMergedPropsEqual`](Function):當`pure`爲`true`,比較`mergeProps `的結果和其先前值。默認值:`淺比較` - [`storeKey`] (String):讀取`store`的地方的`context`的`key`。可能你只有在不建議的具備多個`store`的位置纔會須要這個。默認值:`store`
mapStateToProps
和mapDispatchToProps
的參數數量決定了他們是否接收ownProps
注意:在位於前面的mapStateToProps
和mapDispatchToProps
函數定義時只有一個強制參數(函數長度爲1)的狀況下,ownProps
不會傳遞給這兩個函數。好比,把函數定義爲如下形式時不會接受ownProps
做爲其第二個參數。
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 }
在option.pure
爲true
時優化connect
當option.pure
爲true
時,connect
會進行一系列的等性判斷來避免沒必要要的對mapStateToProps
、mapDispatchToProps
和mergeProps
的調用,以及最終的render
。這些判斷包括areStatesEqual
, areOwnPropsEqual
, areStatePropsEqual
, and areMergedPropsEqual
。儘管大多數狀況下默認判斷均可以適用,可是你也許處於性能或其餘緣由須要重寫這些判斷。下面是幾個例子:
mapStateToProps
函數計算開銷大、而且老是關心一小部分的state
時,你也許但願重寫areStatesEqual
。舉個例子:areStatesEqual: (next, prev) => prev.entities.todos === next.entities.todos
。這樣一來,即可以有效地忽略無關的state
變化,而只關心你那一小部分state
。store state
突變的不純淨reducers
,那麼你可能想重寫areStatesEqual
以使它老是返回false
(areStatesEqual: () => false
)。(這樣可能會影響你的其餘等性判斷,取決於你的mapStateToProps
函數)areOwnPropsEqual
把未來的props
列入白名單。你還要實現mapStateToProps
、mapDispatchToProps
和mergeProps
把props
列入白名單。(實現方式還能夠更簡單,好比使用重構的mapProps
)mapStateToProps
使用了一種只在一個相關prop
變化時才返回一個新對象的memoized selector
,你可能想重寫areStatePropsEqual
使其使用strictEqual
。這可能僅是一個很小的性能提高,但卻能夠省去每一次mapStateToProps
調用時對每個props
進行等性判斷。selectors
製造了一個複雜的props
,你可能想去重寫areMergedPropsEqual
來實現deepEqual
深比較。好比:嵌套對象,新數組等。(深比較會比直接re-rendering
要更快)一個高階React組件類,它可以把state
和action creators
傳遞給由所提供的參數生成的組件中去。這一高階組件由connectAdvanced
生成,這裏會把它的詳情羅列出來。
實例
僅注入dispatch
而不監聽store
export default connect()(TodoApp)
注入全部action建立函數(addTodo
,completeTodo
,...)而不訂閱store
import * as actionCreators from './actionCreators' export default connect(null, actionCreators)(TodoApp)
注入dispatch
以及全局state的全部字段
不要這麼作!這將會扼殺全部的性能優化,由於TodoApp
會在每一次state變化後從新渲染。最好把更多細粒度的connect()
用在你的每一個展現層裏的組件中去監聽相關的一小部分state
export default connect(state => state)(TodoApp)
注入dispatch
和todos
function mapStateToProps(state) { return { todos: state.todos } } export default connect(mapStateToProps)(TodoApp)
注入todos
和全部的action建立函數
import * as actionCreators from './actionCreators' function mapStateToProps(state) { return { todos: state.todos } } export default connect(mapStateToProps, actionCreators)(TodoApp)
注入todos
和全部的action建立函數(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建立函數(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
建立函數(addTodo
and 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(Object.assign({}, 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( Object.assign({}, 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)
工廠函數
工廠函數能夠用來進行性能優化
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
中,則不那麼自覺得是。它不對默認值或結果的記錄作任何假設,而是將這些責任留給調用者。
它不修改傳遞給它的組件類;相反,它返回一個新的、已鏈接的組件類,供您使用。
selectorFactory(dispatch, factoryOptions): selector(state, ownProps): props
(Function):初始化選擇器函數 (在每一個實例的構造函數中)。該選擇器函數是在 connector 組件須要從新計算一個新的 props 時調用,做爲 store 的 state 改變或者接收到一個新的 props 的結果。selector 的結果應該是一個普通對象,做爲被包裹的組件的 props 傳遞。若是連續調用 selector 都返回與上一次調用相同的對象(===),則不會從新渲染該組件。selector 的責任是在適當的時候返回之前的對象。[connectOptions
] (Object) 若是指定,則進一步自定義鏈接器(connector
)的行爲。
getDisplayName
] (Function):計算鏈接器組件相對於被包裹的組件的 DisplayName 屬性。 一般被包裹函數覆蓋。 默認值: name => 'ConnectAdvanced('+name+')'
[methodName] (String):顯示在錯誤消息中。 一般被包裹函數覆蓋。 默認值: 'connectAdvanced' - [`renderCountProp`] (String): 若是被定義, 名爲此值的屬性將添加到傳遞給被包裹組件的 props 中。它的值將是組件被渲染的次數,這對於跟蹤沒必要要的從新渲染很是有用。默認值: `undefined` - [`shouldHandleStateChanges`] (Boolean): 控制鏈接器(`connector`)組件是否訂閱 redux store 的 state 更改。 若是設置爲 false,則只會在`componentWillReceiveProps`中從新渲染。 默認值: true - [`storeKey`] (String): 能夠獲取 store 的 `props/context` key。 當你不明智地使用了多個 `store` 的時候,你纔可能須要這個。默認值: `'store'` - [`withRef`] (Boolean): 若是爲 true,則將一個引用存儲到被包裹的組件實例中,並經過 `getWrappedInstance()` 方法使其可用。 默認值:` false` - 此外,經過 `connectOptions` 傳遞的任何額外選項都將傳遞給 `factoryOptions` 參數中的 `selectorFactory`。
一個高階 React 組件類,它從 store 的 state 生成 props
並將它們傳遞給被包裹的組件。高階組件是接受組件參數並返回新組件的函數。
靜態屬性
WrappedComponent
(Component): 原始組件類傳遞給 connectAdvanced(...)(Component)
靜態函數
組件的全部原始靜態方法都被掛起。
實例方法
getWrappedInstance(): ReactComponent
返回被包裹組件的實例。只有當你傳遞 { withRef: true }
做爲options
的參數纔可用。
connectAdvanced
返回一個高階組件,因此須要調用它兩次。 第一次使用上面描述的參數,第二次使用組件: connectAdvanced(selectorFactory)(MyComponent)
。connectAdvanced
不修改傳遞的 React 組件。它返回一個新的鏈接組件,您應該使用它。根據 props
將特定用戶的 todos
注入,並將 pros.userid
注入到操做中
import * as actionCreators from './actionCreators' import { bindActionCreators } from 'redux' function selectorFactory(dispatch) { let ownProps = {} let result = {} const actions = bindActionCreators(actionCreators, dispatch) const addTodo = text => actions.addTodo(ownProps.userId, text) return (nextState, nextOwnProps) => { const todos = nextState.todos[nextOwnProps.userId] const nextResult = { ...nextOwnProps, todos, addTodo } ownProps = nextOwnProps if (!shallowEqual(result, nextResult)) result = nextResult return result } } export default connectAdvanced(selectorFactory)(TodoApp)
createProvider([storeKey])
建立一個新的<Provider>
,它將在上下文的傳遞 key 上設置 Redux Store。 當你不明智地使用了多個 store 的時候,你纔可能須要這個。您還須要將相同的 storeKey 傳遞給connect
的 options
參數。
storeKey
] (String): 要在其上設置 store 的context
的 key。 默認值: 'store'
在建立多個stores以前,請先查看這個FAQ:Can or should I create multiple stores?
import { connect, createProvider } from 'react-redux' const STORE_KEY = 'componentStore' export const Provider = createProvider(STORE_KEY) function connectExtended( mapStateToProps, mapDispatchToProps, mergeProps, options = {} ) { options.storeKey = STORE_KEY return connect( mapStateToProps, mapDispatchToProps, mergeProps, options ) } export { connectExtended as connect }
如今你能夠導入上述的Provider
和connect
來使用他們了。
<Provider/>
<Provider/>
使得每個被connect()
函數包裝過的嵌套組件均可以訪問到Redux store
。
既然任何Redux-React app中的React組件均可以被鏈接,那麼大多數應用都會在最頂層渲染<Provider>
,從而包裹住整個組件樹。
一般,你若是不把已鏈接組件嵌套在<Provider>
中那麼你就不能使用它。
注意: 若是實在是須要,你能夠手動把store
做爲一個prop
傳遞給一個鏈接組件。可是咱們僅建議在單元測試中對 store
僞造(stub),或者在非徹底React代碼庫中才這麼作。一般,你就使用<Provider>
吧。
store
(Redux Store): 應用程序中惟一的 Redux store 對象children
(ReactElement) 組件層級的根組件。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') )
確保在開始以前學習了Redux排錯
這個警告會在你使用react 15.5.*
時出現。基本上,現上它只是一個 warning, 可是在 React16 當中可能會致使你的應用崩潰。如今 PropTypes 應該從 'prop-types' 包中 import,而不是從 react 包中 import。
更新到最新版本的 react-redux。
閱讀上面的連接。 簡而言之,
connect()
的 mapDispatchToProps
參數或者 bindActionCreators
來綁定 action creator 函數,你也能夠手動調用 dispatch()
進行綁定。直接調用 MyActionCreators.addTodo()
並不會起任何做用,由於它只會返回一個 action 對象,並不會 dispatch 它。若是你正在使用 React Router 0.13
,你可能會碰到這樣的問題。解決方法很簡單:當使用 <RouteHandler>
或者 Router.run
提供的 Handler
時,不要忘記傳遞 router state
。
根 view:
Router.run(routes, Router.HistoryLocation, (Handler, routerState) => { // 注意這裏的 "routerState" ReactDOM.render( <Provider store={store}> {/* note "routerState" here */} <Handler routerState={routerState} /> </Provider>, document.getElementById('root') ) })
嵌套 view
render() { // 保持這樣傳遞下去 return <RouteHandler routerState={this.props.routerState} /> }
很方便地,這樣你的組件就能訪問 router 的 state 了! 固然,你能夠將 React Router 升級到 1.0,這樣就不會有此問題了。(若是還有問題,聯繫咱們!)
若是 view 依賴全局的 state 或是 React 「context」,你可能發現那些使用 connect()
進行修飾的 view 沒法更新。
這是由於,默認狀況下 connect() 實現了 shouldComponentUpdate,它假定在 props 和 state 同樣的狀況下,組件會渲染出一樣的結果。這與 React 中 PureRenderMixin 的概念很相似。
這個問題的最好的解決方案是保持組件的純淨,而且全部外部的 state 都應經過 props 傳遞給它們。這將確保組件只在須要從新渲染時纔會從新渲染,這將大大地提升了應用的速度。
當不可抗力致使上述解法沒法實現時(好比,你使用了嚴重依賴 React context 的外部庫),你能夠設置 connect() 的 pure: false 選項:
function mapStateToProps(state) { return { todos: state.todos } } export default connect(mapStateToProps, null, null, { pure: false })(TodoApp)
這樣就表示你的 TodoApp
不是純淨的,只要父組件渲染,自身都會從新渲染。注意,這會下降應用的性能,因此只有在別無他法的狀況下才使用它。
若是你有 context
的問題,
<Provider>
。若是你在 web 中使用 React,就一般意味着你重複引用了 React。按照這個連接解決便可。