React-Redux 中文文档

介绍

快速开始

React-ReduxRedux的官方React绑定库。它可以使你的React组件从Redux store中读取数据,而且向store分发actions以更新数据javascript

安装

在你的React app中使用React-Redux:html

npm install --save react-redux

或者java

yarn add react-redux

Providerconnect

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 listappweb

一个Todo List实例

跳转到:npm

React UI 组件redux

咱们所用到的React UI组件以下:api

  • TodoApp:咱们应用的入口组件,它renderAddTodo,TodoListVisibilityFilters组件
  • 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名为setFilteraction以更新已选过滤条件
  • 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对象(包含了自增的idcontent属性)
    • 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_FILTERaction 时负责更新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-Reduxstore链接到咱们的app中

建立Store

首先咱们须要让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的若干函数组成的对象
    • 若是是一个action creators构成的对象,每个action creator将会转化为一个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/>如今拥有了一个propaddTodo 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。咱们从todosbtIds对象获取到所需信息。然而,咱们也须要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列表中!

https://i.imgur.com/N68xvrG.png

咱们接下来会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参数,你的组件将会:

  • store改变时不从新渲染
  • props形式接收每一个你经过mapDispatchToProps注入的action建立函数,可以在你调用后自动分发actions
import { addTodo } from "./actionCreators";
// ... Component
export default connect(
  null,
  { addTodo }
)(Component);

订阅store而且注入action建立函数

若是你在connect方法中传入了mapStateToPropsmapDispatchToProps,你的组件将会:

  • 订阅mapStateToProps从store中提取的部分值,当这些值改变时会从新渲染
  • props形式接收每一个你经过mapDispatchToProps注入的action建立函数,可以在你调用后自动分发actions
import * 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状态了。立刻就行了!

https://i.imgur.com/4UBXYtj.png

终于,让咱们开始实现VisibilityFilters功能。

<VisiblityFilterse/>组件须要从store中读取当前选中的过滤条件,而且分发actions。所以,咱们须要把mapStateToProps以及mapDispatchToProps都传递给connect方法。mapStateToProps可以做为visiblityFilter状态的一个简单的访问器。mapDispatchToProps会包括setFilteraction建立函数。

// 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。是否是很棒呢?🎉🎊

https://i.imgur.com/ONqer2R.png

连接

获取更多帮助

使用 React-Redux

Connect:使用mapStateToProps抽取数据

做为传递给connect的第一个参数,mapStateToProps用来从store中选择被链接的组件所须要的部分数据。常以mapState缩写来表示。

  • 每当store state改变时就被调用
  • 接收整个store state,而且返回一个组件须要的数据对象

声明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) => { } ) 的形式定义的——它都可以以一样的方式生效。

参数

  1. state
  2. 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方法去抽取和转化数据

咱们强烈建议使用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方法足够快,你应该相关数据改变时从新运行这些复杂的转化。

有下面几种形式来达到这样的目的:

  • 一些转化能够在action建立函数或者reducer中运算,而后能够把转化过的数据储存在store中
  • 转换也能够在组件的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
}

连接和参考

教程

性能

Q&A

Connect: 使用mapDispatchToProps分发actions

做为第二个传入connect的参数,mapDispatchToProps能够实现向store中分发acions。

dispatch是Redux store实例的一个方法,你会经过store.dispatch来分发一个action。这也是惟一触发一个state变化的途径

使用React-Redux后,你的组件就再也不须要直接和store打交道了——connect会为你完成这件任务,React-Redux提供了两种能够分发actions的方式:

  • 默认地,一个已链接组件能够接收props.dispatch而后本身分发actions
  • connect可以接收一个mapDispatchToProps做为第二个参数,这将让你可以建立dispatch调用方法,而后把这些方法做为props传递给你的组件。

分发(Dispatching)的途径

默认:做为一个prop的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。你对dispatchownProps都具备访问权限。你能够借此机会编写你的链接组件的自定义函数。

参数

  1. dispatch
  2. 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接收两个参数:

  1. 一个函数(action creator)或一个对象(每一个属性为一个action creator)
  2. dispatch

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。但你能够经过在mapDispatchToPropsreturn中添加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仅在下面这些状况下注入组件:

  1. 你没有提供mapDispatchToProps

默认的mapDispatchToProps只是简单的dispatch => ({ dispatch })。若是你不提供mapDispatchToPropsdispatch会以上面的形式自动提供给你。

换言之,也就是你这么作了:

//组件接收 `dispatch`
connect(mapStateToProps /** 没有第二参数*/)(Component);
  1. 你自定义的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?

固然。你能够经过给第一个参数传入nullundefined来跳过它。你的组件就不会订阅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

API 参考

API

Provider

使Redux store可用于connect()调用下面组件层次结构中。一般,若是没有<Provider>包装父级或祖先组件,则没法使用connect()

若是你 实在 是须要,你能够手动地将store做为一个prop传递给被链接组件,可是咱们仅建议在单元测试中对store伪造(stub),或者在非彻底React代码库中这么作。一般,你就使用<Provider>吧。

props

  • storeRedux 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')
)

connect

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

将一个React组件链接到Redux Store。connectadvancedConnect的对外提供的一个较为经常使用的API,可以用于大多数场景。

它不会改变传递给它的组件类,而是返回了一个新的、被链接的组件供你使用。

参数

  • [mapStateToProps(state, [ownProps]): stateProps] (Function):

若是指定过了这个参数,那么新组件就会向Redux store订阅更新。这意味着任什么时候候store一旦更新,mapStateToProps就会被调用。mapStateToProps的结果必须是一个纯对象,以后该对象会合并到组件的props

若是你的mapStateToProps函数被声明为接收两个参数,那么第一个参数就是store state,第二个参数就是传递给链接组件的propsmapStateToProps函数也会在链接组件的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,第二个参数就是传递给链接组件的propsmapDispatchToProps函数也会在链接组件的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()会避免从新渲染以及对mapStateToPropsmapDispatchToPropsmergeProps的调用。假设咱们的被包装组件是一个不依赖任何inputstate的“纯”组件,仅依赖于props和被选中的Redux store state除外。默认值:true
    • [areStatesEqual](Function):当puretrue,比较下一个store state和其先前值。默认值:严格相等(===)
    • [areOwnPropsEqual](Function):当puretrue,比较下一个`props
`和其先前值。默认值:`浅比较`
- [`areStatePropsEqual`](Function):当`pure`为`true`,比较`mapStateToProps
`的结果和其先前值。默认值:`浅比较`
- [`areMergedPropsEqual`](Function):当`pure`为`true`,比较`mergeProps
`的结果和其先前值。默认值:`浅比较`
- [`storeKey`] (String):读取`store`的地方的`context`的`key`。可能你只有在不建议的具备多个`store`的位置才会须要这个。默认值:`store`

mapStateToPropsmapDispatchToProps的参数数量决定了他们是否接收ownProps

注意:在位于前面的 mapStateToPropsmapDispatchToProps函数定义时只有一个强制参数(函数长度为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.puretrue时优化connect

option.puretrue时,connect会进行一系列的等性判断来避免没必要要的对mapStateToPropsmapDispatchToPropsmergeProps的调用,以及最终的render。这些判断包括areStatesEqual, areOwnPropsEqual, areStatePropsEqual, and areMergedPropsEqual。尽管大多数状况下默认判断均可以适用,可是你也许处于性能或其余缘由须要重写这些判断。下面是几个例子:

  • 在你的mapStateToProps函数计算开销大、而且老是关心一小部分的state时,你也许但愿重写areStatesEqual。举个例子:areStatesEqual: (next, prev) => prev.entities.todos === next.entities.todos。这样一来,即可以有效地忽略无关的state变化,而只关心你那一小部分state
  • 若是你有使你store state突变的不纯净reducers,那么你可能想重写areStatesEqual以使它老是返回falseareStatesEqual: () => false)。(这样可能会影响你的其余等性判断,取决于你的mapStateToProps函数)
  • 你可能想经过重写areOwnPropsEqual把未来的props列入白名单。你还要实现mapStateToPropsmapDispatchToPropsmergePropsprops列入白名单。(实现方式还能够更简单,好比使用重构的mapProps
  • 若是你的mapStateToProps使用了一种只在一个相关prop变化时才返回一个新对象的memoized selector,你可能想重写areStatePropsEqual使其使用strictEqual。这可能仅是一个很小的性能提高,但却能够省去每一次mapStateToProps调用时对每个props进行等性判断。
  • 若是你的selectors制造了一个复杂的props,你可能想去重写areMergedPropsEqual来实现deepEqual深比较。好比:嵌套对象,新数组等。(深比较会比直接re-rendering要更快)

返回值

一个高阶React组件类,它可以把stateaction creators传递给由所提供的参数生成的组件中去。这一高阶组件由connectAdvanced生成,这里会把它的详情罗列出来。

实例

仅注入dispatch而不监听store

export default connect()(TodoApp)

注入全部action建立函数(addTodocompleteTodo,...)而不订阅store

import * as actionCreators from './actionCreators'

export default connect(null, actionCreators)(TodoApp)

注入dispatch以及全局state的全部字段

不要这么作!这将会扼杀全部的性能优化,由于 TodoApp会在每一次state变化后从新渲染。最好把更多细粒度的 connect()用在你的每一个展现层里的组件中去监听相关的一小部分state
export default connect(state => state)(TodoApp)

注入dispatchtodos

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建立函数(addTodocompleteTodo,...)的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

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 传递给connectoptions 参数。

参数

  • [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 }

如今你能够导入上述的Providerconnect来使用他们了。

Provider

<Provider/>

概览

<Provider/>使得每个被connect()函数包装过的嵌套组件均可以访问到Redux store

既然任何Redux-React app中的React组件均可以被链接,那么大多数应用都会在最顶层渲染<Provider>,从而包裹住整个组件树。

一般,你若是不把已链接组件嵌套在<Provider>中那么你就不能使用它。

注意: 若是实在是须要,你能够手动把store做为一个prop传递给一个链接组件。可是咱们仅建议在单元测试中对 store伪造(stub),或者在非彻底React代码库中才这么作。一般,你就使用<Provider>吧。

Props

  • 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排错

我收到如下警告:Accessing PropTypes via the main React package is deprecated. Use the prop-types package from npm instead.

这个警告会在你使用react 15.5.*时出现。基本上,现上它只是一个 warning, 可是在 React16 当中可能会致使你的应用崩溃。如今 PropTypes 应该从 'prop-types' 包中 import,而不是从 react 包中 import。

更新到最新版本的 react-redux。

个人视图不更新啊!

阅读上面的连接。 简而言之,

  • Reducer 永远不该该更改原有 state,应该始终返回新的对象,不然,React Redux 觉察不到数据变化。
  • 确保你使用了 connect()mapDispatchToProps 参数或者 bindActionCreators 来绑定 action creator 函数,你也能够手动调用 dispatch() 进行绑定。直接调用 MyActionCreators.addTodo() 并不会起任何做用,由于它只会返回一个 action 对象,并不会 dispatch 它。

React Router 0.13 的 route 变化中,view 不更新

若是你正在使用 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,这样就不会有此问题了。(若是还有问题,联系咱们!)

Redux 外部的一些东西更新时,view 不更新

若是 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 或 props 中都找不到 “store”

若是你有 context 的问题,

  1. 确保你没有引入多个 React 实例到页面上。
  2. 确保你没有忘记将根组件或者其余祖先组件包装进 <Provider>
  3. 确保你运行的 React 和 React Redux 是最新版本。

Invariant Violation:addComponentAsRefTo(...):只有 ReactOwner 才有 refs。这一般意味着你在一个没有 owner 的组件中添加了 ref

若是你在 web 中使用 React,就一般意味着你重复引用了 React。按照这个连接解决便可。