React 和 Redux没有直接关系,但是二者都是通过state来描述界面的状态,因此redux可以和react很好地结合使用
React-Redux将所有组件分成两大类:UI组件和容器组件,UI组件负责页面的呈现,容器组件负责管理数据和逻辑
UI组件的特征:
- 只负责UI的呈现,不带有任何业务逻辑
- 没有状态(不使用this.state这个变量)
- 所有数据都由参数(this.props)提供
- 不使用任何Redux的API
容器组件的特征:
- 负责管理数据和业务逻辑,不负责UI的呈现
- 带有内部状态
- 使用Redux的API
如果一个组件既有UI又有业务逻辑,则可以将其拆分成这样的结构:
- 外面是一个容器组件,里面包含一个UI组件
- 外面的容器组件负责与外部的通信,将数据传给里面的UI组件,然后UI组件渲染出视图
connect()方法:Redux提供connect方法,用于从UI组件生成容器组件
import { connect } from 'react-redux'
const VisibleTodoList = connect()(TodoList) //TodoList是UI组件,VisibleTodoList是react-redux通过connect方法生成的容器组件
该容器组件包含以下两部分的业务逻辑:
- 输入逻辑:外部的数据(state对象)如何转换为UI组件的参数
- 输出逻辑:用户发出的动作如何变为action对象,从UI组件转出去
import { connect } from 'react-redux'
const VisibleTodoList = connect( //connect方法接收两个参数:mapStateToProps 和 mapDispatchToProps
mapStateToProps, //负责输入逻辑,将state映射到UI组件的参数(props)
mapDispatchToProps //负责输出逻辑,将用户对UI组件的操作映射成action
)(TodoList)
mapStateToProps方法:建立从state对象到UI组件的props的映射关系
const mapStateToProps = (state) => {
return{ //mapStateToProps函数返回一个对象
todos:state.todos //返回的对象有一个todos属性,对应UI组件的同名参数
}
}
mapStateToProps方法会订阅store,每当state更新的时候,就会自动执行,重新计算UI组件的参数,从而触发UI组件的重新渲染
mapDispatchToProps方法:建立UI组件的参数到store.dispatch方法的映射,即定义哪些用户的操作应该当作action,传给store
const mapDispatchToProps = (dispatch) => {
return{ //返回一个对象,对象的每个键值对都是一个映射,定义了UI组件的参数怎样发出action
increment: () => { dispatch(increment()) }
}
}
<Provider>组件
connect方法生成容器组件后,需要让容器组件拿到state对象,才能生成UI组件的参数
React-Redux提供Provider组件,可以让容器组件拿到state
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import reducer from './reducer'
import App from './App'
let store = createStore(reducer)
render(
<Provider store={store}> //Provider组件中包含了App组件,使得App组件的所有子组件也可以拿到state
<App/>
</Provider>,
document.getElementById('root')
)
实例:计数器
创建Counter组件
import React from 'react'
import { connect } from 'react-redux'
import incrementAction from '../redux/actions/actionCreators'
export default class Counter extends React.Component{
render(){
const { value,increment } = this.props
return(
<div>
<span>{value}</span>
<br/>
<button onClick={increment}>+</button>
</div>
)
}
}
该UI组件的参数:
//定义从state到value的映射
const mapStateToProps = (state) => {
return{
value:state.count
}
}
//定义从increment到dispatch的映射
const mapDispatchToProps = (dispatch) => {
return{
increment:()=>{
dispatch(incrementAction)
}
}
}
使用connect方法生成容器组件:
export default connect(mapStateToProps,mapDispatchToProps)(Counter)
对代码拆分:将store、reducer、action拆分成一个个文件,放到store文件夹中
store/reducer.js
function counter(state={count:0},action){
const count = state.count
switch(action.type){
case 'increment':
return {count:count+1};
default:
return state;
}
}
export default counter
store/index.js
import { createStore } from 'redux'
import reducer from './reducer/reducer'
const store = createStore(reducer);
export default store;
store/actionCreations.js
const incrementAction = {
type:'increment'
}
export default incrementAction
使用Provider组件:
ReactDOM.render(
<Provider store={store}>
<Counter/>
</Provider>,
document.getElementById('root')
);