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。按照这个连接解决便可。