1、搭配 React
Redux 默认并不包含 React 绑定库,需要单独安装。
npm install --save react-redux
1.1、容器组件(Smart/Container Component)和展示组件(Dumb/Presentational Component)
Redux 的 React 绑定包含了 容器组件和展示组件分离 的开发思想。因此应该在最顶层组件(如路由操作)里使用 Redux。其余内部组件仅仅是展示性的,所有数据都通过 props 传入。
- 容器组件:最顶层(如路由处理)、从 Redux 获取 state 读取数据、向 Redux 派发 action 修改数据
- 展示组件:中间和子组件、从 props 获取数据、从 props 调用回调函数修改数据
在复杂的应用中,也有可能会有多个容器组件。虽然可以嵌套使用容器组件,但应该尽可能的使用传递 props 的形式。
1.2、连接到 Redux
首先,将顶层组件包装进 React-Redux 提供的 Provider 中。
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import './index.css';
import App from './App';
import rootReducers from './reducers'
let store = createStore(rootReaducers);
ReactDOM.render(
<Provider store = {store} >
<App />
</Provider>,
document.getElementById('root'));
将 store 传入 Provider 使得 store 能为下面的组件所用。
接着,通过 react-Redux 提供的 connect() 方法将包装好的组件连接到 Redux。尽量只做一个顶层组件,或者 route 处理。从技术上来说,你可以将应用中的任何一个组件 connect() 到 Redux store 中,但是尽量避免这么做,因为这个数据流很难追踪。
import React, { Component} from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { addTodo, completeTodo, setVisibilityFilter, VisibilityFilters } from '../actions';
import AddTodo from '../components/AddTodo';
import TodoList from '../components/TodoList';
import Footer from '../components/Footer';
class App extends Component {
render() {
// 通过调用 connect() 注入:
const { dispatch, visibleTodos, visibilityFilter } = this.props
return (
<div>
<AddTodo
onAddClick={text =>
dispatch(addTodo(text))
} />
<TodoList
todos={this.props.visibleTodos}
onTodoClick={index =>
dispatch(completeTodo(index))
} />
<Footer
filter={visibilityFilter}
onFilterChange={nextFilter =>
dispatch(setVisibilityFilter(nextFilter))
} />
</div>
)
}
}
App.propTypes = {
visibleTodos: PropTypes.arrayOf(PropTypes.shape({
text: PropTypes.string.isRequired,
completed: PropTypes.bool.isRequired
})),
visibilityFilter: PropTypes.oneOf([
'SHOW_ALL',
'SHOW_COMPLETED',
'SHOW_ACTIVE'
]).isRequired
}
function selectTodos(todos, filter) {
switch (filter) {
case VisibilityFilters.SHOW_ALL:
return todos;
case VisibilityFilters.SHOW_COMPLETED:
return todos.filter(todo => todo.completed);
case VisibilityFilters.SHOW_ACTIVE:
return todos.filter(todo => !todo.completed);
}
}
// 基于全局 state ,哪些是我们想注入的 props ?
// 注意:使用 https://github.com/reactjs/reselect 效果更佳。
function select(state) {
return {
visibleTodos: selectTodos(state.todos, state.visibilityFilter),
visibilityFilter: state.visibilityFilter
};
}
// 包装 component ,注入 dispatch 和 state 到其默认的 connect(select)(App) 中;
export default connect(select)(App);
任何一个从 connect() 包装好的组件都可以得到一个 dispatch 方法作为组件的 props,以及得到全局 state 中的所需的任何内容。connect() 的唯一参数是 selector。此方法可以从 Redux store 接收到全局的 state,然后返回组件中需要的 props。最简单的情况下,可以返回一个初始的 state。
当使用路由时用 包裹 ,以便路由处理器可以访问 store。
<Provider store={store}>
<Router>
<Route path="/" component={App} />
</Router>
</Provider>
2、API
2.1、<Provider store >
使组件层级中的 connect() 方法能够获得 Redux store,正常情况下,将根组件嵌套在该 中才能使用 connect 方法。
属性
- store(Redux Store):应用程序中的唯一的 Redux store 对象
children(ReactElement):组件层级的根组件
<Provider store = {store} >
<App />
</Provider>,
或
<Provider store={store}>
<Router>
<Route path="/" component={App} />
</Router>
</Provider>
2.2、connect()
connect([mapStateToProps],[mapDispatchToProps],[mergeProps],[options])连接 React 组件和 Redux store。连接操作不会改变原来的组件类,反而返回一个新的已与 Redux store 连接的组件类。
参数
- [mapStateToProps(state,[ownProps]): stateProps] (function):如果定义该参数,组件将会监听 Redux store 的变化。任何时候,只要 Redux store 发生变化,mapStateToProps 函数就会被调用。该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并。如果省略了该参数,组件将不会监听 Redux store。如果指定了该回调函数中的第二个参数 ownProps,该参数的值为传递到组件的 props,而且只要组件接收到新的 props,mapStateToprops也会被调用。
- [mapDispatchToProps(dispatch,[ownProps]):dispatchProps] (Object or Function):如果传递的是一个对象,那么每个定义在该对象的函数都将被当做 Redux action creator,而且这个对象与 Redux store 绑定在一起,其中所定义的方法名将作为属性名,合并到组件的props中。如果传递是的一个函数,该函数将接收一个 dispatch 函数,然后由你来决定如果返回一个对象,这个对象通过 dispatch 函数与 action creator 以某种方式绑定在一起 (可能会用到 Redux 的辅助函数 bindActionCreators())。如果省略了该参数,默认情况下,dispatch 会注入到组件的 props 中。如果指定了该回调函数中的第二个参数 ownprops,该参数的值为传递到组件的 props,而且只要组件接收到新的 props,mapDispatchToProps 也会被调用。
- [mergeProps(stateProps,dispatchProps,ownProps): props] (Function):如果指定了这个参数,mapStateToProps() 与 mapDispatchToProps() 的执行结果和组件自身的 props 将传入到这个回调函数中。该回调函数返回的对象将作为 props 传递到被包装的组件中。可以使用该回调函数,根据组件的 props 来筛选部分的 state 数据,或者把 props 中的某个特定变量与 action creator 绑定在一起。如果省略了该参数,默认情况下返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的结果。
- [options] (Object):如果指定这个参数,可以定制 connector 的行为。
- [pure = ture] (Boolean):如果为 true,connector 将执行 shouldComponentUpdate 并且浅对比 mergeProps 的结果。避免不必要的更新,前提是当前组件是一个纯组件,它不依赖与任何的输入或 state 而只依赖于 props 和 Redux store 的 state。默认值为 true。
- [withRef = false] (Boolean):如果为 true,connector 会包含一个对被包含组件实例的引用,该引用通过 getWrappedInstance() 方法获得,默认值为 false。
返回值: 根据配置信息,返回一个注入了 state 和 action creator 的 React 组件。
静态属性: WrappedComponent (Component):传递到 connect() 函数的原始组件类。
静态方法: 组件原来的静态方法都被提升到被包装的 React 组件。
实例方法: getWrappedInstance(): ReactComponent,仅当 connect() 函数的第四个参数 options 设置了 {withRef: true} 才会返回被包装的组件实例。
备注:
- 函数将被调用两次,第一次是设置参数,第二次是组件与 Redux store 连接:
connect(mapStateToProps, mapDispatchToProps, mergeProps)(MyComponent) - connect 函数不会修改传入的 React 组件,返回的是一个新的已于 Redux store 连接的组件,而是你应该使用这个新组件。
- mapStateToProps 函数接收整个 Redux store 的 state 作为 props,然后返回一个传入到组件 props 的对象。该对象被称之为 selector。(可以使用 reselect 中间件)。