redux调试工具
定义
Redux DevTools是一个开源项目,是为谷歌浏览器用户打造的一款实用调试插件,主要适用于开发者使用,使用该插件可以有效地对应用程序或者网页的状态进行调试操作,这个插件拥有丰富的设置参数和可视化工具,可以随时查看到触发的action的变化,
使用redux devtools
安装redux devtools浏览器插件
更多工具===>扩展程序===>点击加载已解压的扩展程序
安装redux-devtools-extension模块
cnpm i redux-devtools-extension -S
store/index.js
- createStore参数
- 参数一:reducer函数
- 参数二:devtool调试工具函数
// 引入调试工具 composeWithDevTools是一个函数
import {composeWithDevTools} from 'redux-devtools-extension'
// 创建store仓库
const store = createStore(reducer,composeWithDevTools())
// 导出store
export default store
react-redux(重点)
安装
cnpm i react-redux -S
- 提供两个API:
- Provider组件,放在index.js中,组件包住App组件。
- connect()高阶组件,放到容器性组件中。
Provider组件引入
// 导出Provider组件
// Provider: store仓库的提供者
import {Provider} from 'react-redux'
// 导入store
import store from './store'
ReactDOM.render(
<React.StrictMode>
<HashRouter>
<Provider store={store}>
<App />
</Provider>
</HashRouter>
</React.StrictMode>,
document.getElementById('root')
);
connect函数
- 作用:连接组件,形成容器型组件。
- 第一次是方法的调用, 调用该方法时,返回一个函数并接受两个参数。
mapStateToProps
: 将state数据映射到props属性上,该参数是一个函数 。mapDispatchToProps
: 将dispatch方法映射到props属性上,该参数也是一个函数。- 第二次是组件的连接 ,即调用返回的函数,将做为组件传参。
- 返回值: 返回新的组件,新组建称之为:容器型组件
//将派生state数据映射到props属性上,形参state,必须有返回值为一个对象
const mapStateToProps = (state)=>({
//键名自定义,值为从state中导出各自子模块中的状态数据。
list:state.order.list,
info:state.user.info,
})
// 将dispatch方法映射到props属性上,形参dispatch,必须有返回值为一个对象
const mapDispatchToProps = (dispatch)=>({
changeList:(list)=>dispatch(orderActions.changeList(list)),
add:(obj)=>dispatch(orderActions.add(obj)),
del:(index)=>dispatch(orderActions.del(index)),
changeInfo:(info)=>dispatch(userActions.changeInfo(info)),
})
const ConnectIndex = connect(mapStateToProps,mapDispatchToProps)(Index)
export default ConnectIndex
容器型组件
import {connect} from 'react-redux'
const ConnectIndex = connect(mapStateToProps,mapDispatchToProps)(Index)
//ConnectIndex为容器型组件
export default ConnectIndex
展示型组件
import React from 'react'
//函数组件在此时此刻:称之为:展示型组件 ,一般为函数组件。
export default function Order(props) {
const {list,changeList} = props;
return (
<div>
</div>
)
}
容器型组件VS展示型组件
容器型组件 | 展示型组件 | |
---|---|---|
关注点 | 逻辑[取数据,更新数据] | UI的展现 |
对redux是否感知 | 是 | 否 |
读数据 | 从redux的store 中获取 | 从props中获取 |
写数据 | 发送redux actions | 调用props的回调 |
如何创建 | 通过react-redux connect创建 | 函数组件 |
总结
容器型组件: 一般采用类组件进行声明
书写逻辑
对接react-redux
展示型组件:一般采用函数组件进行声明
展示数据
对接props属性
redux高阶(重点)
bindActionCreators
- bindActionCreators是redux的一个自带函数。
- 作用:单个或多个ActionCreator转化为dispatch(action)的函数集合形式。开发者不用再手动写
dispatch(actionCreator(type))
,而是可以直接调用方法,可以实现简化书写,减轻开发的负担。
import {bindActionCreators} from 'redux'
const mapDispatchToProps = (dispatch)=>({
//使用bindActionCreators合并所有的订单子模块中的dispatch,他可以自动将orderActions内所有的方法添加dispatch方法。
orderActions: bindActionCreators(orderActions,dispatch),
})
import React from 'react'
export default function Order(props) {
const {list,
orderActions:{changeList,add,del}//order中触发action所有的方法都在orderActions中。
} = props;
return (
<div>
<button onClick={()=>changeList([{id:1,name:'包子'}])}>获取订单列表</button>
<button onClick={()=>add({id:list.length+1,name:'油条'})}>添加</button>
<hr />
{list.map((item,index)=>(
<div key={item.id}>
{item.name}
<button onClick={()=>del(index)}>删除</button>
</div>
))}
</div>
)
}
selectors
- 用来派生数据。
export const getList = (state)=>state.order.list
export const getFilter = (state) => {
console.log(111);//这里当不是本页面的数据计算,这里也会执行。
const { list, filter } = state.order
return filter === 0 ? list : list.filter(item => item.status == filter)
}
- 组件中:
import {getList} from '../store/modules/order'
//将派生的状态映射到props属性上,必须有返回值为一个对象
const mapStateToProps = (state) => {
return ({
// 键名自定义,值为状态的值
list: getList(state),
})
}
总结:
redux的五大核心: state reducer actions selectors modules
Reselect
为什么使用reselect
- redux让数据流通变得规范、状态管理变得可控。通过用react-redux提供的connect函数, mapStateToProps成为视图组件获取state 的唯一途径。
- 既然mapStateToProps成为组件获取state的唯一途径,那么当我们想要从mapState提供的状态中计算一些需要的数据时,比如计算当前要展示的页面的项目,我们希望展示的页面依赖的list和 filter变化了,才进行重新计算。
- 每次store.state update后,redux都会调用一次mapStateToProps,不论list和filter 的值是否改变。当我们的app足够大、组件众多时,这里面大量冗余计算的问题就值得重视 了。reselect这个库的目的,就是帮我们把这部分的计算提取到一个函数中。
安装
cnpm i reselect -S
createSelector
-
参数一:执行的selector方法
-
参数二:执行selector方法的回调,回调有返回值为派生的数据。
-
作用:过滤派生的数据,使个页面数据变化只变化某个页面的数据。类似modules
//selectors
const getList = (state) => state.order.list // 这里则无需暴露
const getFilter = (state) => state.order.filter
// createSelector是一个函数
import {createSelector} from 'reselect'
// 优化的过滤列表,这里是真正派生的数据
export const getFilterList = createSelector([getList,getFilter],(list,filter)=>{
console.log(11); //这里当不是本页面的数据计算,这里不会执行。
// 参数list为:执行getList回调的初始值
// 参数filter为执行getFilter回调的初始值
return filter === STATUS.ALL ? list : list.filter(item=>item.status===filter)
})
组件:
import {getFilterList} from '../store/modules/order'
const mapStateToProps = (state) => {
return ({
//这里的state,依然会给到getList,getFilter。
list: getFilterList(state),
})
}
redux中间件
Redux middleware
在 applyMiddleware中,可以使用中间件。
import { applyMiddleware} from 'redux'//使用中间件函数
自己封装中间件
封装检阅action中间件
- 我们可以检阅每一个流过的action,并挑选出特定类型的 action 进行相应操作,以此来改变 action。
import { applyMiddleware} from 'redux'
// 封装检阅action中间件
const logger = store=>next=>action=>{
console.log(store);
console.log('要提交的action为:',action);
next(action)//这里不写则阻断。
console.log('提交之后的state状态数据为',store.getState());
}
const store = createStore(reducer,composeWithDevTools(applyMiddleware(logger)))
export default store
异步错误处理中间件
import {applyMiddleware} from 'redux'
//异常错误中间件
const error = store=>next=>action=>{
try{
next(action)
}catch(err){
console.log('error的错误信息为',err);
}
}
const store = createStore(reducer,composeWithDevTools(applyMiddleware(error,logger)))
export default store
redux-logger中间件
1.安装
cnpm i redux-logger -S
2.引入logger
store/index.js
// 引入中间件
import {logger} from 'redux-logger'
const store = createStore(reducer,composeWithDevTools(applyMiddleware(logger)))
export default store
redux-thunk(中间异步处理中间)
1.安装
cnpm i redux-thunk -S
2.store/index.js
// 引入异步处理中间件
import thunk from 'redux-thunk'
const store = createStore(reducer,composeWithDevTools(applyMiddleware(thunk,logger)))
export default store
3.store/modules/user.js
import axios from 'axios'
// 1.声明state
const initialState = {
info: '', //用户信息
indexGoods: [{ content: [] }, { content: [] }, { content: [] }, { content: [] }],
}
// 2.封装actionType
const TYPES = {
"CHANGE_INFO": "CHANGE_INFO",
"CHANGE_GOODS": "CHANGE_GOODS"
}
// 3.封装actionCreator
export const actions = {
changeInfo: (info) => ({ type: TYPES.CHANGE_INFO, info }),
changeGoods: (indexGoods) => ({ type: TYPES.CHANGE_GOODS, indexGoods }),
requestActions: (dispatch) => {
// 发起axios请求
axios.get('/api/getindexgoods').then(res => {
if (res.data.code === 200) {
// 接收dispacth方法, 并自调actions.changeGoods
dispatch(actions.changeGoods(res.data.list))
}
})
}
}
const userReducer = (state = initialState, action) => {
switch (action.type) {
case TYPES.CHANGE_INFO:
return {
...state,
info: action.info
}
// throw new Error('出错了')
case TYPES.CHANGE_GOODS:
return {
...state,
indexGoods: action.indexGoods
}
default:
return state
}
}
//派生
export const getInfo = (state) => state.user.info
export const getGoodsList = (state) => state.user.indexGoods
4.Index.jsx
import React, { Component } from 'react'
// 导出connect方法
import {connect} from 'react-redux'
// 导入订单子模块的actions
import {actions as orderActions,getList,getFilterList} from '../store/modules/order'
// 导入用户中心子模块的actions
import {actions as userActions,getInfo,getIndexGoods} from '../store/modules/user'
// 引入子组件
import Order from './Order'
import User from './User'
// 导入bindActionCreators
import {bindActionCreators} from 'redux'
class Index extends Component {
render() {
const {info,indexGoods,changeInfo,requestGoods} = this.props
return (
<div>
<User info={info} indexGoods={indexGoods} changeInfo={changeInfo} requestGoods={requestGoods} />
</div>
)
}
}
//将state数据映射到props属性上
const mapStateToProps = (state)=>({
info:getInfo(state),
indexGoods:getIndexGoods(state)
})
// 将dispatch方法映射到props属性上
const mapDispatchToProps = (dispatch)=>({
requestGoods:()=>userActions.goodsActions(dispatch)
})
const ConnectIndex = connect(mapStateToProps,mapDispatchToProps)(Index)
export default ConnectIndex
项目目录设计
按照功能划分
Order #订单模块
action.js #actionCreator
reducer.js #reducer函数
Index.js #容器型组件
components
Order.jsx #展示型组件
User #会员中心模块
action.js #actionCreator
reducer.js #reducer函数
Index.js #容器型组件
components
User.jsx #展示型组件
弊端:造成代码冗余,修改比较困难
按照类型设计
action.js #actionCreator
reducer.js #reducer函数
pages
Order.jsx #容器型组件
User.jsx #容器型组件
components
Order.jsx #展示型组件
User.jsx #容器型组件
弊端:修改数据比较困难
ducks设计模式
src
assets
css
js
img
font
pages #一级路由
Login
Login.jsx
login.css
Goods
Index.jsx #容器型组件
component
Goods.jsx #展示型组件
views #二级路由
store
index.js #store仓库
modules
order.js #订单子模块[state | actionCreator | actionType | reducer | selector]
user.js #会员中心子模块[[state | actionCreator | actionType | reducer | selector]]
reuquest
http.js
api.js
utils
router
index.js
App.jsx