react-redux模型图
- 所有的UI组件都应该包裹一个容器组件,他们是父子关系
- 容器组件是真正和redux打交道的,里面可以随意的使用redux的api
- UI组件中不能使用任何redux的api
- 容器组件会传给UI组件:1.redux中所保存的状态;2.用于操作状态的方法
- 容器组件给UI组件传递状态和操作状态的方法均通过props传递
react-redux核心知识点
1.利用connect创建一个容器组件连接UI组件和redux
import {connect} from 'react-redux'
export default connect(mapStateToProps,mapDispatchToProps)(UI组件名)
2.connect的第一个参数mapStateToProps
- mapStateToProps函数返回的是一个对象
- 返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
- mapStateToProps用于传递redux中的状态
function mapStateToProps(state){
return {
count:state
}
}
3.connect的第二个参数mapDispatchToProps
- mapDispatchToProps函数返回的是一个对象
- 返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
- mapDispatchToProps用于传递redux中操作状态的方法
function mapDispatchToProps(dispatch){
return{
increment:(data) => dispatch(incrementAction(data)),
decrement:(data) => dispatch(decrementAction(data)),
incrementAsync :(data,time) => dispatch(incrementActionAsync (data,time)),
}
}
4.index.js入口文件不需要再用store.subscribe进行检测刷新
- react-redux内部可以自己检测到redux状态更改
...
ReactDOM.render(<app/>,document....)
/*store.subscribe(() => {
ReactDOM.render(<app/>,document....)
}) */
react-redux完整代码案例
我们接下来就用react-redux去改造上章用redux写的案例,下面是虽然把所有文件全部引入过来,但是改进时基本上只操作了容器组件(这里我把容器组件和其UI组件写在一个文件里,为了方便管理和维护)
效果图:
Count组件 (改动)
- 在文件内加入容器组件,并暴露出去,之前的组件作为其UI组件,
- 容器组件与redux交互,UI组件内部只调用容器组件传入的状态和方法
import React from "react";
import {connect} from 'react-redux'
//UI组件
class CountUI extends React.Component{
state = {}
increment = () => {
const {value} = this.selectNumber
this.props.increment(value*1)
}
dencrement= () => {
const {value} = this.selectNumber
this.props.decrement(value*1)
}
incrementIfOdd= () => {
const {value} = this.selectNumber
const {count} = this.props
if(count % 2 !== 0){
this.props.increment(value*1)
}
}
incrementAscyn= () => {
const {value} = this.selectNumber
this.props.incrementAsync(value*1,500)
}
render(){
return(
<div>
<h2>当前求和:{this.props.count}</h2>
<select ref={c => this.selectNumber = c}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.dencrement}>-</button>
<button onClick={this.incrementIfOdd}>当前总数为奇数+</button>
<button onClick={this.incrementAscyn}>异步+</button>
</div>
)
}
}
//此时store仓库只关联了Count组件的reducer,所以redux中只有一个状态值,所以state就是count
//后面我们会讲到多个组件共享数据,到时候store会保存多个组件的reducer,我们会通过redux中一个api去合并所有的reducer产生的值到一个对象里
function mapStateToProps(state){
return {
count:state
}
}
function mapDispatchToProps(dispatch){
return{
increment:(data) => dispatch(incrementAction(data)),
decrement:(data) => dispatch(decrementAction(data)),
incrementAsync :(data,time) => dispatch(incrementActionAsync (data,time)),
}
}
//容器组件
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)
store.js(未改动)
/*该文件专门用于暴露一个store对象,整个应用只有一个store对象*/
import {createStore,applyMiddleware} from "redux"
//引入为Count组件服务的reducer
import reducer from "./reducers"
//引入redux-thunk中间件,用于支持异步action
import thunk from "redux-thunk"
export default createStore(reducer,applyMiddleware(thunk))
constant.js(未改动)
/* 该模块用于定义action对象中type类型的常量值,
便于管理和维护的同时,防止程序员单词写错*/
export const INCREMENT = "increment"
export const DECREMENT = "dencrement"
count_action.js (未改动)
/* 该文件专门为Coun组件生成的action对象;
action的值为对象是同步,为函数是异步*/
import {INCREMENT,DECREMENT} from './constant'
import store from './redux/store'
export const incrementAction = data => ({type:INCREMENT,data})
export const decrementAction = data => ({type:DECREMENT,data})
export const incrementActionAsync = (data,time) => {
//返回一个函数,函数能开启异步任务
return (dispatch) => {
setTimeout(() => {
dispatch(incrementAction(data))
},time)
}
}
count_reducer.js (未改动)
/* 1.该文件用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
2.reducer函数会传入两个参数:分别为:之前的状态(previousState),动作对象(action)
*/
//redux初始化状态
import {INCREMENT,DECREMENT} from './constant'
const initState = 0 initState = {count:0}
export defaulr function countReducer(preState=initState,action){
const {type,data} = action
switch(type){
case INCREMENT:
return preState + data
case DECREMENT:
return preState - data
default:
return preState
}
}
app.jsx(改动)
- 实际没有改动代码,只是此时的Count 组件不是UI组件,而是容器组件
import React ,{Component} from 'react'
//引入容器组件
import Count from ./Count
import store from './redux/store'
export default class APP extends Component {
render(){
<div>
<Count />
</div>
}
}
index.js入口文件(改动)
- 利用react-redux中的provider方法给App下所有需要store的容器组件传入store,使容器组件与redux关联起来
- 删除之前的检测改动,react-redux自动检测刷新
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import store from './store'
//会给App下所有需要store的容器组件传入store
import {provider} from 'react-redux'
ReactDOM.render(
<provider store={store}>
<app/>
<provider/>
,document.getElementById('root'))
/*为了redux内部状态发生改变,就重新渲染组件,不用担心一个组件内部状态改变,其他组件会被渲染,因为diff算法,只会重新渲染状态更改的组件*/
/*store.subscribe(() => {
ReactDOM.render(<app/>,document....)
}) */
容器组件简化版代码
- mapDispatchToProps作为第二个参数可以写作一个对象
- mapDispatchToProps对象的value值只需要调用action返回一个动作对象即可,react-redux会自动dispatch分发
import React from "react";
import {connect} from 'react-redux'
//UI组件
class CountUI extends React.Component{
...
}
//容器组件
export default connect(
state => ({count:state}),
{
increment:incrementAction,
decrement:decrementAction,
incrementAsync:incrementActionAsync
}
)(CountUI)