在项目中引入redux管理数据
1.添加redux相关依赖
yarn add redux react-redux redux-thunk
- react-redux(react组件和redux结合) 比如context,provider共享store
- redux-thunk 进行异步请求
2.在store文件夹中创建store
store > index.js
2.1 createStore传入的参数
1.reducer
1.项目中的reducer不止一个,因为模块多,不可能所有数据都通过一个reducer进行处理,一般一个模块一个reducer
2.多个reducer必然需要合并
store>reducer.js
import { combineReducers } from 'redux';
const cReducer = combineReducers({
})
export default cReducer;
3.在index.js引入reducer.js
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(reducer, );
export default store;
2.enhancer, 对store进行加强
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
//为了让调试工具的redux起作用,还需要使用composeEnhancers包裹applyMiddleware
const store = createStore(reducer, composeEnhancers(
applyMiddleware(thunk)
//applyMiddleware 合并多个中间件
));
export default store;
3.App.js导入Provider,store
这样项目就可以使用共享的store了
import React, { memo } from 'react';
import { Provider } from 'react-redux'
import { renderRoutes } from 'react-router-config';
import { HashRouter } from 'react-router-dom'
import routes from './router';
import store from './store'
import AppHeader from 'components/app-header'
import AppFooter from 'components/app-footer'
export default memo(function App() {
return (
<Provider store = {store}>
<HashRouter>
<AppHeader />
{renderRoutes(routes)}
<AppFooter />
</HashRouter>
</Provider>
)
})
4.运行项目报警告,combineReducers中没有有效reducer,创建一个recommend的reducer
为了架构更清晰,一个模块一个store,需要用的时候就可以直接用
- 1.在recommend目录下新建了store文件夹,
actionCreators.js:调用接口,对返回数据进行处理和储存,
constants.js:action的名称,
index.js:导出reducer,
reducer.js:对数据进行合并
- 2.reducer.js
默认状态,reducer方法:根据action(常量)更新state
import * as actionTypes from './constants';
const defualtState = {
topBanners: []
}
function reducer(state = defualtState, action) {
switch(action.type) {
case actionTypes.CHANGE_TOP_BANNERS:
return {...state, topBanners: action.topBanners }
default:
return state;
}
}
export default reducer;
- 3.constants.js
放action 常量
export const CHANGE_TOP_BANNERS = "recommend/CHANGE_TOP_BANNERS";//recommend为模块名
- 4.index.js
导出reducer
import reducer from './reducer';
export {
reducer
}
- 5.全局store里导入模块的reducer
import { combineReducers} from 'redux';
import { reducer as recommendReducer } from '../pages/discover/c-pages/recommend/store'
const cReducer = combineReducers({
recommend: recommendReducer
})
export default cReducer;
已经可以在浏览器里redux Chrome插件里看到topBanners
5.在recommend组件里发送网络请求
redux-thunk发起网络请求——>拿到数据data——>通过dispatch传递action对象(包含数据)——>reducer进行数据合并
5.1 拿到数据data的过程
dispatch(函数),函数写在actionCreators里面
import * as actionTypes from './constants'
import { getTopBanners } from '@/services/recommend'
const changeTopBannerAction = (res) => ({
type: actionTypes.CHANGE_TOP_BANNERS,
topBanners: res.banners
})
export const getTopBannerAction = () => {
return dispatch => {
getTopBanners().then(res => {
dispatch(changeTopBannerAction(res))//拿到数据,放到reducer里面,需要新的action:changeTopBannerAction
})
}
// 为什么要传入action返回的函数而不是action赋值的函数,因为可能会需要根据传入的参数生成新的函数
}
发送网络请求的时候不会直接在actionCreators中使用request,而是导入对应模块的请求文件。
对于每一个模块可能会发送多个网络请求,所以一个模块一个文件来存放对应的网络请求url等数据。可以增强项目的扩展性。
import request from './request';
export function getTopBanners() {
return request({
url: "/banner"
})
}
5.2 在recommend组件中如何获取dispatch和state
mapStateToProps ,mapDispatchToProps ,connect
import React, { memo, useEffect } from 'react'
import { connect } from 'react-redux' //返回高阶组件
import { getTopBannerAction } from './store/actionCreators'
function XLRecommend(props) {
const { getBanners, topBanners } = props;
useEffect(() => {
getBanners()
}, [getBanners])
return (
<div>
{topBanners.length}
</div>
)
}
const mapStateToProps = state => ({//最完整的state
topBanners: state.recommend.topBanners
});
const mapDispatchToProps = dispatch => ({
getBanners: () => {
dispatch(getTopBannerAction())
}
});
export default connect(mapStateToProps, mapDispatchToProps)(memo(XLRecommend));
6.用redux的hooks优化和redux的关联代码
- 如果很多个页面都需要和redux关联起来,这样代码就有点麻烦,所以需要优化
- 组件和redux的关联需要做:
6.1 获取数据
const {topBanners} = useSelector(state => ({topBanners: state.recommend.topBanners}));
6.2 进行dispatch操作
const dispatch = useDispatch();
import React, { memo, useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { getTopBannerAction } from './store/actionCreators'
function XLRecommend() {
const {topBanners} = useSelector(state => ({topBanners: state.recommend.topBanners}));
const dispatch = useDispatch();
useEffect(() => {
dispatch(getTopBannerAction());
}, [dispatch])
return (
<div>
{topBanners.length}
</div>
)
}
export default memo(XLRecommend);
6.3 useSelector性能优化
import { useSelector, useDispatch, shallowEqual } from 'react-redux'
const { topBanners } = useSelector(state => ({ topBanners: state.getIn(["recommend", "topBanners"]) }), shallowEqual);
使用useSelector会出现,组件没有使用某个状态,但是状态改变组件会重新渲染,原因是useSelector返回的是一个函数,
而且内部默认做的是全等比较,因为函数每次返回值都不同,所以每次都会触发重新渲染。但是我们希望是做个浅层比较(shallowEqual) {a:aaObj,b:bbObj}
如果是全等比较,没有使用aa,bb状态的组件,在aabb改变时组件会重新渲染,state改变后。
而浅层比较,不会重新渲染,只是比较key有没有变化。
6.4 immutable优化reducer中拷贝旧数据
reducer里的数据在变化时需要拷贝旧数据,所产生的性能和内存消耗问题
return {...state, topBanners: action.topBanners }
- 管理的数据转化为immutable
- 安装 immutable
- 选择map还是fromJS,fromJS进行的深层比较,没有什么必要,所以选择map
import { Map } from 'immutable'
...
return state.set("topBanners",action.topBanners)//会返回新的对象
- combineReducers不能使用Map,因为无法直接获取key值,需要安装新的依赖: redux-immutable,可以
把原来的对象转化为immutable对象
import { combineReducers} from 'redux-immutable';
import { reducer as recommendReducer } from '../pages/discover/c-pages/recommend/store'
const cReducer = combineReducers({
recommend: recommendReducer
})
export default cReducer;