写在前面
在现在我对于redux的了解基本就属于会做一些简单操作,但是真正在开发中去做一写请求什么的,绝壁狗die,所以我来重新整理一次开发中redux的使用过程,理理思路。虽然现在我不会,maybe,写完这个文章我就豁然开朗了。
------------------------------------------------------------------------------------------------------------------------------------
第一部分首先说说redux各部分都是干什么的?
首先,众所周知,Vue有属于自己的Vuex,作为自己的状态管理神器。
那么react必然也会有自己的状态管理神器。没错,就是redux。
redux的出现,就是为了方便我们管理众多复杂的状态,同时也有利于我们去管理代码,维护代码。
对于redux而言,其下主要有这么几个小弟。
first—>store
store主要负责做什么呢?
store这个弟弟,其主要职责就是通过createStore创建一个store数据点。
seconde —> action
那么action主要负责做什么呢?
action的作用类似于一个古代驿站,全国所有的信件都必须从我这里分发,因此action都是作用于修改数据的,redux官方明确指出,在redux中,必须通过派发(dispatch)action来修改state。
end —> reducer
这个时候肯定有人问了,store和action具体怎么沟通呢?
别着急,古代的驿站都有信使收发信件呢。高端大气上档次的redux怎么会没有个“信使”。
没错,reducer就是用来处理store和action的。
reducer是一个纯函数,通过将state和action结合起来返回一个新的state。这样一次数据的修改就完成了。
这时候问题来了,方便吗?方便吗?方便吗? 肯定是方便的。如果有一天你读过别人的代码,读过自己之前写的代码,你会发现,越复杂的事情,最后一块一块的解析成一个个小规模,就差不多像工厂流水线一样,各自有各自的工作和职责,各司其职,就ok了。
好了,废话不多说。 我直接上项目开发中使用redux的过程了。
------------------------------------------------------------------------------------------------------------------------------------
第二部分: 基本项目开发中我们对redux文件的划分
(1)redux目录结构的划分。
我们日常的开发中,需要将redux代码安排在一个文件夹中。
首先我们创建一个store文件夹。
紧接着,需要创建 index.js,constants.js,reducer.js,actionCreators.js
(2)目录结构如下
(3)那么每个部分用于做什么呢?
- reducer.js 这里负责写好reducer纯函数,
import {
ADD_NUMBER,
SUB_NUMBER
} from './constants.js'
const initialState = {
counter:0
}
function reducer(state = initialState, action){
switch(action.type) {
case ADD_NUMBER:
return {...state, counter : state.counter + action.num}
case SUB_NUMBER:
return {...state, counter : state.counter - action.num}
default:
return state;
}
}
export default reducer;
- actionCreators.js 这里我们将需要派发的action封装为更加灵活的函数。
//将action封装为更加灵活的函数
import {
ADD_NUMBER,
SUB_NUMBER
} from './constants.js'
export const addAction = num => {
return {
type:ADD_NUMBER,
num
}
}
export const subAction = num => {
return {
type:SUB_NUMBER,
num
}
}
- constants.js 这里为了放置我们的变量由于使用不统一造成的问题,我们将我们要使用的变量定义为常量。
export const ADD_NUMBER = "ADD_NUMBER";
export const SUB_NUMBER = "SUB_NUMBER";
- index.js 这里写好store,同时导出页面,供其他组件使用.
import redux from 'redux'
import reducer from './reducer.js'
const store = redux.createStore(reducer);
export default store;
这样看下来,redux貌似也就这个样子,但是当我去真正开项目开发中,redux的使用的时候,我又发现是我草率了。
我有草率了。
草率的总是我!!!!
------------------------------------------------------------------------------------------------------------------------------------
第三部分: 首先在项目中对redux来个简单配置
(1)首先作为一个react初级程序yuan,想必创建如下文件夹就不用本yuan多说了。
(2)接着,既然我们想要使用redux,那就先添加两个库,redux,以及react-redux,以及redux-thunk,想必大家必定用过,但是可能具体作用记不起来了。
redux — > 状态管理器
react-redux —> 里面有context 以及provider (共享stroe)
redux-thunk —> 做异步请求
(3)ok!还算简单吧! 接下里就进入到我们第一部创建好的文件中。
首先我们去store/index.js 中愉快的创建我们的store数据点。
(4)首先在store下的index.js中我们要去创建store数据点。
index.js 1.0
//index.js
import { createStore } from 'redux'
const store = createStore();
reducer.js 1.0
根据我上一篇文章积累下来的经验,每一个通过createStore创建出来的store,都需要传入一个reducer对象。
再动动我们的小脑子,一个那么大的项目,肯定有好多数据需要派发管理,所以一个reducer是远远不够的,那么就体现了拆分reducer的重要性,这个上篇文章也讲过了。
import { combineReducers } from 'redux';
const cReducer = combineReducers({
})
export default cReducer;
其中,从redux引入的conbinReducers 负责合并各个模块的reducer。
接下来,我们就可以在我们index.js 1.0中引入了我们的reducer了。
index 2.0
import { createStore } from 'redux';
import cReducer from './reducer'
const store = createStore(cReducer);
export default store;
在接下来,store中还可以在传入一个参数,可以使用一些中间件,类如redux-thunk,用来处理异步请求。
index3.0
import { createStore,applyMiddleware } from 'redux';
import thunk from 'redux-thunk'
import cReducer from './reducer'
const store = createStore(cReducer,applyMiddleware(thunk));
export default store;
这样就算我们对使用redux前的一些简单配置完成了。
接下来使用store进行共享
这个就比较简单了,我们之间安装了react-redux,其中提供了Provider来实现组件间共享store中的状态。
这个我们之间去最外层的App.js中
//首先去react-redux中导入 Provider
//之后去 store中导入我们刚才封装好的数据点store
//最后使用Provider组件,包裹我们页面所有的组件,这样就实现了我们的redux管理我们绝大多数的数据状态。
其中Provider组件中要传入 键值对 store={store}
import { Provider } from 'react-redux'
import store from './store'
<Provider store={store}>
//这里是你所有的组件
</Provider>
这样,我们项目中要使用的redux基本的配置,就已经完成了。
但是浏览器回报给我们一个这样的错误。
用我这种没过四级的英语loser都知道,他说我reducer合并的时候返回了个寂寞。所以这个问题先不着急,也不会导致你目前的项目页面渲染。
怎么样,到了这里,我想你已经觉得,就这???
就这??
redux就这?
这个博主居然写两篇这么长的文章。
真为这个博主汗颜。
那我只能说,too young too simple !
真正的redux难点在下一部分。
------------------------------------------------------------------------------------------------------------------------------------
第四部分: 使用redux进行请求数据和管理数据
到了这里,我想大家已经对使用redux开发兴趣满满了,前面我们准备了这么久,想必大家跃跃欲试。那么,我们就来一次数据请求吧。
(1)而对于数据请求而言,我们每一个页面模块的数据请求都应该封装在一个store下,下面同时设置 index.js actionCreators.js constant.js reducer.js 。
(2)目录结构如下
这是一个recommend页面模块下的store文件夹。
(3)现在文件建好了,在recommend模块内部,我们先开始写哪一个文件呢?
(以下文件均为recommend/store下的文件,大家别定义错文件)
巧了,没错,就是reducer.js
reducer.js 1.0
const defaultState = {
topBanners:[]
}
function reducer(state = defaultState, action) {
switch(action.type) {
case
}
}
好了,到这里reducer.js 1.0就算完成了,这时候,就差有人来敲我脑袋了,你给我管这个叫做敲完了????
case 后面接你妹啊???
啊 害。。。 别着急。
看了第一篇文章,大家都知道这里的case主要作用于去派发不同的action,因此我们需要去定义不同的action,同时,为了避免我们使用常量错误,所以首先要去constant.js中去定义一些我们要使用的常量。
接下来我们来实现constant.js 1.0 ,因为我们只需要演示一个数据状态的改变,所以示例只有一个常量,不过对于学习redux毫无影响,甚至还有点想笑。
constatnt.js
export const CHANGE_TOP_BANNERS = "CHANGE_TOP_BANNERS"
然后,我们就已经做好了很多事情了,对就这,已经做好了很多事情了。
接下来,我们常量定义好了,可以回过头去完善我们的reducer.js了。
类比一下我们可不是抛妻弃子的渣男,赚钱了就回家养家了。
so 来吧。
reducer.js 2.0
import * as actionTypes from './constants'
const defaultState = {
topBanners:[]
}
function reducer(state = defaultState, action) {
switch(action.type) {
case actionTypes.CHANGE_TOP_BANNERS:
return {...state,topBanners:[]}
default:
return state;
}
}
相比于reducer.js 1.0,我们做了什么呢?
首先引入我们的常量,方便我们去识别action,从而派发action。
接下来,我们干了什么呢?
我们定义了默认数据defaultState用来存储我们要请求回来的数据,并且复制为数组类型。
而reducer函数内部做了什么呢?
它通过分析我们dispatch派发的action的type来使用switch case进行判断,从而实现数据的修改。
接下来干什么呢?
不妨去index.js里把我们定义好的reducer分享出去,这样在我们目录框架下的store/reducer里,可以实现reducer的合并。
recommend/store/index.js 1.0
import reducer from './reducer'
export {
reducer
}
接下来意味这,我们recommend模块里的reducer算是一个50%的完成状态,这个时候我们可以去目录框架下的store/reducer.js中去合并所有模块的reducer了。
根目录下 store/reducer.js 2.0(这里相较于上面的部分,应该是2.0了奥)
import { combineReducers } from 'redux';
import { reducer as recommendReducer } from '../pages/discover/c-pages/recommend/store';
const cReducer = combineReducers({
recommend:recommendReducer
})
export default cReducer;
这里相较于根目录下 store/reducer.js 1.0我们做了什么呢?
首先我们引入了我们刚才在recommen下定义好的reducer,并且我们通过重命名的方式将其修改名称(为什么修改名称,我们说了,模块多的时候,每次封装的都叫做reducer,what should chrome do?)
接着我们只需要将recommend中的reducer引入我们的最终合并的reducer中即可。
------------------------------------------------------------------------------------------------------------------------------------
接下来我们开始来分发网络请求,同时通过派发action,实现数据在redux中进行管理,和在页面上进行渲染。
又因为我们同时需要在封装action的同时去发起网络请求,所以我们应该先去services下创建对应的recommend.js 去封装好recommend要请求的数据,同时暴露出来一个函数,给我们recommend/store/actionCreators.js文件调调用。
service/recommend.js
/**
* 这里封装所有的recommend下的数据请求
*
*/
import request from './request'
export function getTopBanners() {
return request({
url:'/banner'
})
}
好了,recommend就像一个工具人一样,自己啥都干了,最后孩子是别人的。
胜利果实被窃取了。
所以,接下来我们要去写我们的action了,没有错,就是去actionCreators.js中去封装我们所有要使用到的action。
recommend/store/actionCreators.js 1.0
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));
})
}
};
那么,在如上的代码中,我们主要做了什么呢?
首先引入了在constant.js中定义的常量,方便我们的action在reducer中进行划分,其次引入了我们axios请求封装好的函数。
在接下来,我们说我们导出的getTopBannerAction,首先,在这个函数中我们进行了数据请求,并且将请求回来的数据同时通过上面的changeTopBannerAction,派发了出去。
ok,这个文件要做的事情已经完成了。
那么接下来,我们就要去我们的recommend页面里面拿到我们的数据了。
首先来讲一下,react-redux中的connect函数。
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])(MyComponent)
connect是连接React组件与Redux store,建立组件与store.state和dispatch的映射关系。连接操作不会改变原来的组件类。返回一个新的已于Redux store连接的组件类。
mapStateToProps:
mapStateToProps是一个函数,用于建立组件和store.state的映射关系
如果定义了该参数,组件将会监听redux store的变化。只要redux store发生变化,mapStateToProps函数就会被调用。
mapDispatchToProps:
mapDispatchToProps用于建立组件和store.dispatch的映射关系
mapDispatchToProps可以是对象或函数
recommend/index.js
import React, { memo, useEffect } from 'react';
import { connect } from 'react-redux';
import { getTopBannerAction } from './store/actionCreators'
function KJHRecommend(props) {
console.log(props)
const { getBanners, topBanners} = props;
useEffect(() => {
getBanners();
}, [getBanners])
return (
<div>
<h2>KJHRecommend : {topBanners.length}</h2>
</div>
)
}
const mapStateToProps = state => ({
topBanners: state.recommend.topBanners
});
const mapDispatchToProps = dispatch => ({
getBanners: () => {
dispatch(getTopBannerAction())
}
})
export default connect(mapStateToProps,mapDispatchToProps)(memo(KJHRecommend));
接着,只需要将recommend/store/reducer.js
中代码替换为如下代码即可。
return {...state,topBanners:action.topBanners}
接着·,或许这个文件下面处理action,获取数据的过程过于繁琐,既然大家学习了react hooks。所以我们不妨优化一下代码。
recommend/index.js 2.0
import React, { memo, useEffect } from 'react';
import { connect, useDispatch, useSelector } from 'react-redux';
import { getTopBannerAction } from './store/actionCreators'
function KJHRecommend(props) {
// 组件和redux关联: 获取数据和进行操作
const dispatch = useDispatch();
// 发送网络请求
useEffect(() => {
dispatch(getTopBannerAction());
}, [dispatch])
//拿到数据
const {topBanners} = useSelector(state => ({
topBanners: state.recommend.topBanners
}))
return (
<div>
<h2>KJHRecommend : {topBanners.length}</h2>
</div>
)
}
export default memo(KJHRecommend);
最后,我们通过在useEffect中调用函数,发起请求,同时保存state状态,并且将我们拿到的数据topBanners显示在页面上。
------------------------------------------------------------------------------------------------------------------------------------
写在后面
好了,这样我们redux配置,以及redux的发送请求,保存数据就复习了一遍了,
嗯,博主脑子,貌似有那么点感觉了。
貌似吧。
以后多看看应该就懂了。
有些事情努力是有作用的,有些事情别人说了永远不可能,或许最开始努力到最后都是个百分之0.