React-Redux
一 React-Redux的应用
1.学习文档
英文文档: https://redux.js.org/
中文文档: http://www.redux.org.cn/
Github: https://github.com/reactjs/redux
2.Redux的需求
- 状态的集中管理
- 任意页面与组件之间的数据传递
- 状态管理的可预测
- 数据的本地化缓存提升性能
随着对JavaScript单页应用程序的要求变得越来越复杂,我们的代码必须比以前更多地处理状态。此状态可以包括服务器响应和缓存数据,以及本地创建的尚未保存到服务器的数据。 UI状态的复杂性也在增加,因为我们需要管理活动路线,选定标签,旋钮,分页控件等。 管理这个不断变化的状态是困难的。
如果一个模型可以更新另一个模型,那么一个视图可以更新一个模型,该模型会更新另一个模型,而这又可能导致另一个视图更新。在某种程度上,您不再理解您的应用程序会发生什么情况,因为您已经失去了对其状态的何时,为何和如何的控制。
当系统不透明且不确定时,很难重现错误或添加新功能。 好像这还不够糟糕,请考虑新的要求在前端产品开发中变得常见。作为开发人员,我们需要处理乐观的更新,服务器端渲染,在执行路由转换之前获取数据等等。
我们发现自己试图去处理一个我们以前从来没有处理过的复杂问题,而且我们不可避免地提出这个问题:是放弃的时候了吗?答案是不。
这种复杂性很难处理,因为我们正在混合两个对人类头脑非常难以推理的概念:突变和异步性。我把它们叫做曼托斯和可乐。两者在分离方面都很出色,但它们一起造成一团糟。像React这样的库试图通过去除异步和直接DOM操作来解决视图层中的这个问题。但是,管理数据的状态由您决定。这是Redux进入的地方。
3.什么是Redux
- redux是一个独立专门用于做状态管理的JS库(不是react插件库)
- 它可以用在react、angular、vue等项目中,但基本与react配合使用
- 作用:集中式管理react应用中多个组件共享的状态
那么,什么是Redux呢?Redux 是 JavaScript 状态容器,提供可预测化的状态管理, 可以让你构建一致化的应用,并运行于不同的环境,包括客户端、服务器、原生应用等,并且易于测试。Redux 除了和 React 一起用外,还支持其它界面库,比如React Native, 并且它体小而精悍,只有2kB,包括依赖。
之前已经提及Redux其实是Flux思想的一种表现,而Facebook官方提供的Flux数据流管理类库,其文档就是十分的繁琐,操作起来让人精神崩溃,所以社区力量下诞生了以Flux为基础思想的Redux状态管理器框架。其中Redux的简单和有趣的编程体验是最吸引众人的地方。
Redux异常的简单。和其它的FLUX实现不一样,Redux只有唯一的state状态树,不管项目变的有多复杂,我也仅仅只需要管理一个State树,这就让管理的目标变得单一可控。当然,可能你会有疑问,一个state状态树就够用了?这个state树该有多大?别着急,Redux中的Reducer机制可以解决这个问题。
Redux操作轻松有趣。忙于迭代项目的你,体会编程带来的趣味是有多久没有体会到了?Redux可以通过redux-logger,redux-devtools等调试工具,直接的查看项目状态的操作走向以及当前状态的数据内容,整个应用的action和state都可以被轻松管理?行为还能被保存,删除,回滚,重置!修改了代码,页面不刷新也能产生变化!
总的来说,Redux就是一个组件之间数据传递分享的状态管理器框架。
那么我们什么时候使用它呢?
简单说,如果你的UI层非常简单,没有很多互动,Redux 就是不必要的,用了反而增加复杂性。
比如,用户的使用方式非常简单,用户之间没有协作,不需要与服务器大量交互,也没有使用 WebSocket,视图层(View)只从单一来源获取数据等。
而如果用户的使用方式复杂,不同身份的用户有不同的使用方式(比如普通用户和管理员),多个用户之间可以协作,与服务器大量交互,或者使用了WebSocket,View要从多个来源获取数据等多交互、多数据源情况下,则需要使用Redux。并且,如果某个组件的状态,需要共享,某个状态需要在任何地方都可以拿到,一个组件需要改变全局状态,一个组件需要改变另一个组件的状态是必定使用到Redux。
发生上面情况时,如果不使用 Redux 或者其他状态管理工具,不按照一定规律处理状态的读写,代码很快就会变成一团乱麻。你需要一种机制,可以在同一个地方查询状态、改变状态、传播状态的变化。
当然,也不要把 Redux 当作万灵丹,如果你的应用没那么复杂,就没必要用它。另一方面,Redux 只是 状态管理架构的一种解决方案,除了redux还可以选择其他方案,比如reflux、mobx等。
4.redux工作流程主要包含哪些层次,每个层次的作用是什么
- Store仓库层,数据中心
- Reducers,数据初始与修改
- Component视图层,用以显示数据,动作触发
- ActionCreators,动作派发执行
5.redux的三大基本原则是什么
- 单一数据源
- State是只读的
- 使用纯函数来执行修改
- 纯函数:输入的内容是什么,输出的内容仍旧是什么
- 非纯函数:输入的内容与输出的内容不同,输入的内容再利用非纯函数处理以后将会被改变
- 所有有关时间的
- 所有有关随机的
- 所有有关唯一值的
- 所有有关I/O处理的(input/output) fs->fileSystem
- 不要修改原来的数据按理说应该用纯函数处理,但是我们记不到哪些是纯函数,哪些是非纯函数。所以最简单的办法是将原来的操作对象复制一份。(深拷贝,浅拷贝)
6.什么情况下需要使用redux
- 总体原则: 大型项目状态管理复杂才用
- 某个组件的状态,需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
二 Redux 基础示例
需要理解的概念有:
- redux的结构层次:
1 store:唯一,只有一颗状态树
2 resucers:有state,只读的,通过纯函数修改
3 component:显示与事件的触发
4 action:动作派发
- redux的核心API有哪些
- createStore:创建包含指定reducer的store对象
- store对象:redux库最核心的管理对象,内部维护着state、reducer
- getState 获取状态
- dispatch 派发动作
- subscribe 订阅事件
- applyMiddleware:应用上基于redux的中间件(redux不支持异步更新,只支持同步数据的修改)
- combineReducers:合并多个reducer函数
1.从视图层开始,视图组件里应该显示数据,所以需要有仓库;
2.可以创建仓库,而仓库是唯一的,里面的状态树也是唯一的
安装 npm i redux --save
3.在store仓库中需要有reducer进行初始化与修改,而第一个参数是state状态(null或者对象),第二个参数是action派发的动作操作
**注意:**reducers是数据操作中心,所以现在需要有reducers是需要放到Store的仓库里的
4.可以先返回状态内容,那么就可以利用store.getState()获取到状态数据,以便显示
index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
//reducers的初始化与修改 3
const reducer = (state = { count: 0 }) => {
return state;
};
//创建仓库 2 4
const store = createStore(reducer);
//视图就是组件 1
class App extends Component {
render() {
console.log(store) //有getState属性
//4 获取状态数据
const { count } = store.getState();
return (
<div>
<p>count:{count}</p>
<button>计数</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
5.除了数据显示,则需要进行相应的操作(修改数据),所以会有按钮点击等事件操作,需要触发相应的方法;
6.事件方法需要做的事情就是告知仓库进行指定任务的操作,它本身只是派发任务操作,仅仅是一个指令的发送,并不执行具体的任务操作;
7.派发指定到reducer,由reducer进行确认是否能够执行该任务,并且进行具体的工作内容;(reducer是数据初始化与修改的中心)
8.执行完任务以后,需要由仓库进行监听订阅的操作,以便确认用户在视图组件中发生了内容的改变。
9 需要在组件卸载的时候进行取消订阅的操作
index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
//3 reducers的初始化与修改 返回初始状态
const reducer = (state = { count: 0 }, action) => {
console.log(action) //在action有 type:'INCREASE'
switch (action.type) {
case 'INCREASE': //7 派发后由动作接收并进行具体的内容
return {
count: state.count + 1,
};
default:
return state;//返回count的默认值
}
};
//创建仓库 2 4 将状态给到仓库
const store = createStore(reducer);
//1 视图就是组件 在视图层里应该显示数据(数据都在仓库里)
class App extends Component {
increase = () => { //6
//对增加的事件进行一个动作的派发,派单是只动口不动手
store.dispatch({ type: 'INCREASE' });
};
//执行完动作派发以后到到reducer进行数据修改完,组件是不更新的,仓库进行订阅监听
componentDidMount() { //8
store.subscribe(() => {
//一个组件被修改的前提:要么修改state,要么修改props
this.setState({});//组件的二次渲染
});
}
render() { //5 创建事件
const { count } = store.getState();
return (
<div>
count:{count}
<button onClick={this.increase}>计数</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
三 React-Redux 的流程
词汇表:
- store–>mapStateToProps(状态转属性)
- store-reducers–>combinRudecers(reducer的合并)
- actions–>mapDispatchToProps(方法转属性)
- 容器组件=connet(mapStateToProps,mapDispatchToProps)(视图组件)
- Provider(提供者)提供store
需要理解的概念有:
- Redux 与 React-Redux 的差异
- React-Redux 的流程
为什么需要使用 React-Redux
在 Redux 使用中,组件和 Redux Store 打交道,读取 Store 的状态,用于初始化组件的状态,同时还要监听 Store 的状态改变;当 Store 状态发生变化时,需要更新组件状态,从而驱动组件重新渲染;当需要更新 Store 状态时,就要派发 action 对象;而且需要根据当前 props 和 state,渲染出用户界面。这个操作过程事情太多了,不容易理解、操作与管理。
React-Redux 的流程是如何
在正式尝试示例项目之前还遗留了一此问题,因为已经了解了 redux 的一些设计原则,并且清楚了 Store,State,Action 以及 Reducer 之间的相互关系,但却没有理清楚整个应用之间的流程。为什么呢?因为我们并不清楚 Action 是谁派发的,用户在 UI 界面上会触发哪些操作,用户界面与 Action 之间又是怎么样的关系等问题,还有待进一步的梳理。
一个项目流程的起始点从哪开始我想是梳理项目流程最关键的因素之一。对于 Reac 与 React-Redux 之间的流程走向,最基础的还应该是从用户界面开始,毕竟,对用户来说,他们并不关心应用内部发生了什么,只会关心界面上有哪些体现。因而应用可能会包含很多的 Component 组件内容,当然,在组件与模块不断增多的情况下,可以利用路由来进行统一的管理操作,通过路由切换将不同模块展现给用户。那么,路由、组件这两个重要对象与之前与提的 Store、State、Action、Reducer 又有何关系呢?
第一步:这里需要提出一个内容叫 Provider,也就是提供者,它将会包含整个路由对象内容,但这一提供者给路由、组件到底提供什么内容呢?对于 Redux 来说,没有其它内容提供,唯有 State 需要进行传递,所以利用 Provider 将整个 Store 仓库进行包含,这样路由下的每一个组件模块都将接收到由 Store 仓库至上而下传递过来的唯一 State 状态树数据内容。但是 Store 仓库中的 State 与组件之间没有直接的关联,如何将 State 的数据与组件之间建立联系呢?可以利用 react-redux 中的 mapStateToProps 将 state 状态内容转化为 props 属性,这样组件就可以尝试获取到传递过来的属性对象了。
第二步:在应用的界面上可能会有搜索、按钮等交互性组件对象,用户需要通过对按钮的操作进行一定内容的响应。显然,按钮的点击也好,搜索的提交也罢,这些行为必然应该触发一定的事件内容,那么,这一事件又从哪里来,又该向哪去呢?先解决动作从哪来的问题!这时牵扯的就是 Action 动作层,我们需要创建一些函数,这些函数执行的工作比较简单,就只进行一些操作的派发,也就是 dispatch。用户点击按钮提交搜索其目的是什么?当然是改变显示的数据内容,但这时组件与方法之间的联系还没有完全的建立,需要通过 mapDispatchToProps 将派发方法内容转成 Props 属性对象,这样组件就可以尝试获取到传递过来的方法的属性对象了。不过需要注意的是 redux 只支持同步操作,如果进行异步操作则需要 middleware 中间件进行协助,并且中间件的内容有很多,包括 redux-logger,redux-devtools 等等,那么可以将数据请求,日志输出、调试操作等中间件内容进行顺序执行,在完成以后再通过 Action 进行任务的派发操作。
第三步:现在已经将 State 状态转成了 Props 属性,还把 Action 派发的方法也转成了 Props 属性,但这些属性内容与组件还没有真正建立起关联,想要将这三者进行串连需要通过 react-redux 中的 connect 进行连接,通过 connect 将 mapStateToProps 与 mapDispatchToProps 这两者的属性对象连接到当前的可视化组件中,这样就形成了一个具有数据与方法属性的容器组件内容。
第四步:既然当前组件已经有了数据属性与方法属性,那么就可以直接将数据属性渲染到 UI 组件当中,并且给按钮、搜索等对象绑定方法属性内容。通过事件触发调用对应的方法属性,不过这时候需要将 Action 动作操作派发到 Reducer 中对数据进行纯函数的处理,在派发的过程中可能会传递一些参数 payload,当然也可以一同传递下去,在 Reducer 中当然需要接收这些参数的内容。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BnRyqyGu-1624175998595)(C:\Users\angi\AppData\Roaming\Typora\typora-user-images\image-20210607195539495.png)]
四 React-Redux基础示例
需要理解的概念有:
- React-Redux显示数据的7步走步骤
- React-Redux动作派发执行的12步走步骤
1.将数据显示与视图层
1.需要有一个视图组件以便显示相应的数据
2.可以创建一个store仓库,用以给组件提供数据支持
3.store仓库本身是没有数据的,所以需要有reducer给它设置数据,需要与store进行结合
4.reducer的state是无法实现数据传递的,因为它是状态,但是,它又需要与组件进行沟通,所以需要将state转成props才能进行属性传递
5.reducer的state转props是通过mapStateToProps实现的
安装 npm react-redux
6.props属性与组件之间还没有建立起联系,所以需要利用connect进行关联
7.需要有一个Provider提供者,提供store仓库给所有的组件,所以容器组件的外部需要有Provider进行包裹
2.动作的派发执行
8.视图层中有一个按钮,而这个按钮想要触发一个事件
9.事件就是动作,动作就是函数,可以定义一个mapDispatchToProps,因为事件是无法进行传递的,唯一能够传递的内容只有属性props,而且需要注意的是事件一定会有多个,mapDispatchToProps应该是什么?是对象!
10.mapDispatchToProps也需要利用connect与组件建立起关联
11.视图组件中就可以调用该方法,但是mapDispatchToProps只做派发操作,不做具体动作执行
12.具体的操作执行交由reducer完成(reducer是数据的处理中心,数据的初始化,修改都在reducer中完成)
index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider, connect } from 'react-redux';
// 3有reducer给它设置数据与store进行结合,12具体的操作执行交由reducer完成
const reducer = (state = { count: 0 }, action) => {
switch (action.type) {
case 'INCREASE':
return {
count: state.count + 1,
};
default:
return state;
}
};
// 2创建一个store仓库,4将state转成props才能进行属性传递
const store = createStore(reducer);
class App extends Component {
render() {
console.log(this.props) //increase
const { count, increase } = this.props;
return (
<div> //8 视图层中有一个按钮,而这个按钮想要触发一个事件
count:{count} <button onClick={increase}>计数</button>
</div>
);
}
}
// 5reducer的state转props是通过mapStateToProps实现的
const mapStateToProps = (state) => {
return {
//将state.count状态里的count转为count,也就是属性里的count
count: state.count,
};
};
// 9 事件就是动作,动作就是函数,可以定义一个mapDispatchToProps,因为事件是无法进行传递的,**唯一能够传递的内容只有属性props**
// 11 mapDispatchToProps只做派发操作,不做具体动作执行
const mapDispatchToProps = {
increase() {
return {
type: 'INCREASE',//type的内容是不可变的
};
},
};
// 6 props属性与组件之间还没有建立起联系,所以需要利用connect进行关联,原来状态已经转为props属性了,但是与component还没有建立联系,所以需要利用从connect将属性与组件建立起关联,最后变成的仍旧是一个组件。App是组件,connect是高阶函数,AppContainer是高阶组件
// 10 mapDispatchToProps也需要利用connect与组件建立起关联
const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App);
// 7 需要有一个Provider提供者,提供store仓库给所有的组件(同路由的统一入口)
ReactDOM.render(
<Provider store={store}>
<AppContainer />
</Provider>,
document.getElementById('root')
);
五 React-Redux的目录结构拆分
需要理解的概念有:
- 更清晰的目录结构
- 不同层次的作用
1.仓库层的拆离—单数,有且只有一个(store)
src/store/index.js
import { createStore } from 'redux';
// reducer层的拆离
import reducer from '../reducers/count';
const store = createStore(reducer);
export default store;
2.reducers层的拆离—复数,会有很多 —combinReducers
src/reducers/count.js
const reducer = (state = { count: 0 }, action) => {
switch (action.type) {
case 'INCREASE':
return {
count: state.count + 1,
};
default:
return state;
}
};
export default reducer;
3.动作层的拆离—复数(actions)
src/actions/count.js
export const increase = () => {
return {
type: 'INCREASE',
};
};
4.拆离层次的整合
index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider, connect } from 'react-redux';
// 仓库层的拆离
import store from './store';
// 动作层的拆离
import { increase } from './actions/count';
class App extends Component {
render() {
const { count, increase } = this.props;
return (
<div>
count:{count} <button onClick={increase}>计数</button>
</div>
);
}
}
//一般情况state转props不需要拆分
const mapStateToProps = (state) => {
return {
count: state.count,
};
};
const mapDispatchToProps = { increase };
const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App);
ReactDOM.render(
<Provider store={store}>
<AppContainer />
</Provider>,
document.getElementById('root')
);
六 React-Redux多个状态值的设置(出错,只能改一个)
需要理解的概念有:
- 多个状态值处理时出现的问题(state={count:0,count1:0} 单个到多个)
- 需要利用纯函数进行数据的修改
- 纯函数与非纯函数的差异
- 对象复制操作的必要性
对象复制的方式有哪些?
① Object.assgin
② …对象展开与对象合并
③ lodash(cloneDeep)
1 所有的操作分成两大类:
1)数据的显示
2) 事件的触发
2 所有的显示都在组件开始,组件想显示什么
3 想要显示的内容一定是在仓库
4 仓库里有reducers的初始数据
5 mapStateToProps进行转换
6 connect进行关联
7 Provider进行仓库数据的提供
2 想要修改数据就需要事件的触发
3 触发事件就必须要创建对应的事件,而这个事件就在action层
4 需要引入这个事件并且进行mapStateToProps的转换
5 需要在组件中进行引入与调用
6 需要进行派发到reducers进行数据的修改
src/reducers/count.js
const reducer = (state = { count: 0, count1: 0 }, action) => {
switch (action.type) {
case 'INCREASE':
return {
//状态是只读的,但是现在的操作模式是直接修改状态
count: state.count + 1,
};
case 'DECREASE':
return {
count1: state.count1 - 1,
};
default:
return state;
}
};
export default reducer;
src/actions/count.js
export const increase = () => {
return {
type: "INCREASE",
};
};
export const decrease = () => {
return {
type: "DECREASE",
};
};
index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider, connect } from 'react-redux';
import store from './store';
import { increase, decrease } from './actions/count';
class App extends Component {
render() {
const { count, count1, increase, decrease } = this.props;
return (
<div>
count:{count} <br />
count1:{count1} <br />
<button onClick={increase}>递增</button>
<button onClick={decrease}>递减</button>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
count: state.count,
count1: state.count1,
};
};
const mapDispatchToProps = { increase, decrease };
const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App);
ReactDOM.render(
<Provider store={store}>
<AppContainer />
</Provider>,
document.getElementById('root')
);
七 React-Redux对象合并处理数据(10)
需要理解的概念有:
- 需要利用纯函数进行数据的修改
- 纯函数(slice)与非纯函数(splice)的差异
- 对象复制操作的必要性
- 数据的浅拷贝与深拷贝
src/reducers/count.js
const reducer = (state = { count: 0, count1: 0 }, action) => {
switch (action.type) {
case 'INCREASE':
// 错误方式
// return {
// count: state.count + 1,
// };
// Object.assign对象合并
// return Object.assign({}, state, { count: state.count + 1 });
return { ...state, count: state.count + 1 };
case 'DECREASE':
// 错误方式
// return {
// count1: state.count1 - 1,
// };
// Object.assign对象合并
// return Object.assign({}, state, { count1: state.count1 - 1 });
return { ...state, count1: state.count1 - 1 };
default:
return state;
}
};
export default reducer;
八 React-Redux异步请求的实现
http://jsonplaceholder.com 测试专用
需要理解的概念有:
- 异步操作需要利用中间件支持
- 异步中间件有哪些:redux-thunk、redux-promise、redux-saga
- 函数返回函数并以函数作为参数传递的操作
操作流程:
1 想要在视图上放置一个按钮,而这个按钮的功能将是请求一个接口数据
2 需要进行的是mapDispatchToProps方法转属性
3 那么就需要有以后方法来支持请求数据的这件事,这个方法假如是fetchList,那么在这个函数中则需要进行fetch/axios的请求操作
4 可以进行fetch/axios的请求,但是拿到的数据不会无缘无故到reducers层,所以需要进行派发操作
5 所以fetchList函数可以通过return将dispatch方法进行参数化传递
6 reducers层进行数据的对象合并操作
7 程序是报错的,因为redux只支持同步更新不支持异步更新
8 需要利用中间件进行处理
9 所有中间件操作统一再仓库(thunk applyMiddleWare)
src/reducers/count.js
const reducer = (state = { count: 0, count1: 0, list: [] }, action) => {
switch (action.type) {
case 'INCREASE':
return { ...state, count: state.count + 1 };
case 'DECREASE':
return { ...state, count1: state.count1 - 1 };
case 'GETlISTS'://payload 有效载荷
return { ...state, list: action.payload };
default:
return state;
}
};
export default reducer;
src/actions/count.js
export const increase = () => {
return {
type: 'INCREASE',
};
};
export const decrease = () => {
return {
type: 'DECREASE',
};
};
export const getListData = (payload) => {
return { //接收dispatch(getListData(data))的数据
type: 'GETlISTS',
payload,
};
};
//redux不支持异步更新,如果需要异步更新,则需要第三方插件的支持
export function fetchList() {
//返回的是一个函数,他是一个高阶函数 dispatch就是store里的dispatch函数
//返回的函数是一个函数,这个参数是dispatch,而dispatch它的类型是函数
return (dispatch) => {//把这个函数作为参数再进行调用
let url = 'https://jsonplaceholder.typicode.com/posts';
fetch(url)
.then((res) => {
return res.json();
})
.then((data) => {
dispatch(getListData(data));
});
};
}
src/store/index.js
import { createStore, applyMiddleware } from "redux";
import reducer from "../reducers/count";
import thunk from "redux-thunk";
const store = createStore(reducer, applyMiddleware(thunk));
export default store;
index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider, connect } from 'react-redux';
import store from './store';
import { increase, decrease, fetchList } from './actions/count';
class App extends Component {
render() {
const { count, count1, list, increase, decrease, fetchList } = this.props;
return (
<div>
count:{count} <br />
count1:{count1} <br />
list:{JSON.stringify(list)}
<button onClick={increase}>递增</button>
<button onClick={decrease}>递减</button>
<button onClick={fetchList}>获取数据</button>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
count: state.count,
count1: state.count1,
list: state.list,
};
};
const mapDispatchToProps = { increase, decrease, fetchList };
const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App);
ReactDOM.render(
<Provider store={store}>
<AppContainer />
</Provider>,
document.getElementById('root')
);
九 React-Redux的loading数据请求中的实现
需要理解的概念有:
- loading状态的实现
- error错误的思考
src/reducers/count.js
const reducer = (state = { count: 0, count1: 0, list: [], loading: false }, action) => {
switch (action.type) {
case 'INCREASE':
return { ...state, count: state.count + 1 };
case 'DECREASE':
return { ...state, count1: state.count1 - 1 };
case 'GETlISTS':
return { ...state, list: action.payload };
case 'SHOWLOADING':
return { ...state, loading: action.payload };
default:
return state;
}
};
export default reducer;
src/actions/count.js
export const increase = () => {
return {
type: 'INCREASE',
};
};
export const decrease = () => {
return {
type: 'DECREASE',
};
};
export const getListData = (payload) => {
return {
type: 'GETlISTS',
payload,
};
};
export const showLoading = (payload) => {
return {
type: 'SHOWLOADING',
payload,
};
};
export function fetchList() {
return (dispatch) => {
//数据没有请求前改变loading的state状态值
dispatch(showLoading(true));
let url = 'https://jsonplaceholder.typicode.com/posts';
fetch(url)
.then((res) => {
return res.json();
})
.then((data) => {
dispatch(getListData(data));
dispatch(showLoading(false));
});
};
}
index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider, connect } from 'react-redux';
import store from './store';
import { increase, decrease, fetchList } from './actions/count';
class App extends Component {
render() {
const { count, count1, list, loading, increase, decrease, fetchList } = this.props;
return (
<div>
count:{count} <br />
count1:{count1} <br />
{loading ?
<div>数据请求中...</div> :
<div>list:{JSON.stringify(list)}</div>}
<button onClick={increase}>递增</button>
<button onClick={decrease}>递减</button>
<button onClick={fetchList}>获取数据</button>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
count: state.count,
count1: state.count1,
list: state.list,
loading: state.loading,
};
};
const mapDispatchToProps = { increase, decrease, fetchList };
const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App);
ReactDOM.render(
<Provider store={store}>
<AppContainer />
</Provider>,
document.getElementById('root')
);
十 React-Redux结合路由实现多页面数据共享
需要理解的概念有:
-
路由的应用 静态路由表,分配地址 统一入口 寻址 过滤
-
路由项目结构
1 入口文件 index.js
① 通过Router进行路由的统一入口的设置
② store是唯一的,只有一个状态数,并且可以通过Provide进行store对象的提供
③ 可以将Provider与store对象的合并
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RzXc74Ol-1624175998598)(C:\Users\angi\AppData\Roaming\Typora\typora-user-images\image-20210608092307137.png)]
2 主组件 App.js
3 Home About Count pages
4 components
-
状态的分享
src/pages/Home.js
import React, { Component } from 'react';
class Home extends Component {
render() {
return <div>Home</div>;
}
}
export default Home;
src/pages/About.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
class About extends Component {
render() {
const { count } = this.props;
return <div>About Count:{count}</div>;
}
}
const mapStateToProps = (state) => {
return {
count: state.count,
};
};
const AboutContainer = connect(mapStateToProps)(About);
export default AboutContainer;
src/pages/Count.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { increase, decrease, fetchList } from '../actions/count';
class Count extends Component {
render() {
const { count, count1, list, loading, increase, decrease, fetchList } = this.props;
return (
<div>
count:{count} <br />
count1:{count1} <br />
{loading
? <div>数据请求中...</div>
: <div>list:{JSON.stringify(list)}</div>}
<button onClick={increase}>递增</button>
<button onClick={decrease}>递减</button>
<button onClick={fetchList}>获取数据</button>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
count: state.count,
count1: state.count1,
list: state.list,
loading: state.loading,
};
};
const mapDispatchToProps = { increase, decrease, fetchList };
const CountContainer = connect(mapStateToProps, mapDispatchToProps)(Count);
export default CountContainer;
App.js
import React, { Component } from 'react';
import Home from './pages/Home';
import About from './pages/About';
import Count from './pages/Count';
import { NavLink, Switch, Route, BrowserRouter as Router } from 'react-router-dom';
class App extends Component {
render() {
return (
<Router>
<div>
<ul>
<li>
<NavLink to='/' exact>
首页
</NavLink>
</li>
<li>
<NavLink to='/about'>关于我们</NavLink>
</li>
<li>
<NavLink to='/count'>统计页面</NavLink>
</li>
</ul>
<Switch>
<Route path='/' component={Home} exact />
<Route path='/about' component={About} />
<Route path='/count' component={Count} />
</Switch>
</div>
</Router>
);
}
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import App from './App';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
十一 React-Redux中combineReducers的作用
需要理解的概念有:
- combineReducers的作用
- reducers合并后的结构改变
- action层的合并与拆分
src/reducers/update.js
const update = (state = { update: 0 }, action) => {
switch (action.type) {
case 'INCREASE':
return { ...state, update: state.update + 1 };
default:
return state;
}
};
export default update;
src/reducers/index.js
import { combineReducers } from 'redux';
import count from './count';
import update from './update';
//合并对象后,对象的层次会加深
export default combineReducers({
count,
update,
});
src/actions/index.js
export { increase, decrease, fetchList } from './count';
//先将 increase, decrease, fetchList 三个函数从conut模块中进行接口引入
//再直接将引入的这三个函数进行接口暴露
src/pages/About.js
import React, { Component } from "react";
import { connect } from "react-redux";
class About extends Component {
render() {
const { count } = this.props;
return <div>About Count:{count}</div>;
}
}
const mapStateToProps = (state) => {
return {
count: state.count.count,
};
};
const AboutContainer = connect(mapStateToProps)(About);
export default AboutContainer;
src/pages/Count.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { increase, decrease, fetchList } from '../actions';
class Count extends Component {
render() {
const { count, count1, update, list, loading, increase, decrease, fetchList } = this.props;
return (
<div>
count:{count} <br />
count1:{count1} <br />
update:{update}
<br />
{loading ?
<div>数据请求中...</div>
: <div>list:{JSON.stringify(list)}</div>}
<button onClick={increase}>递增</button>
<button onClick={decrease}>递减</button>
<button onClick={fetchList}>获取数据</button>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
count: state.count.count,
count1: state.count.count1,
list: state.count.list,
loading: state.count.loading,
update: state.update.update,
};
};
const mapDispatchToProps = { increase, decrease, fetchList };
const CountContainer = connect(mapStateToProps, mapDispatchToProps)(Count);
export default CountContainer;
src/store/index.js
import { createStore, applyMiddleware } from 'redux';
import reducers from '../reducers';
import thunk from 'redux-thunk';
const store = createStore(reducers, applyMiddleware(thunk));
export default store;
十二 React-Redux数据持久化操作
https://github.com/rt2zz/redux-persist
需要理解的概念有:
- 数据持久化的必要性
- 本地存储的方式有哪些
src/store/index.js
import { createStore, applyMiddleware } from 'redux';
import reducers from '../reducers';
import thunk from 'redux-thunk';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage'; // defaults to localStorage for web
const persistConfig = {
key: 'root',
storage,
};
const persistedReducer = persistReducer(persistConfig, reducers);
export const store = createStore(persistedReducer, applyMiddleware(thunk));
export const persistor = persistStore(store);
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { Provider } from 'react-redux';
import { store, persistor } from './store'; //从一个对象变成两个对象
import App from './App';
import { PersistGate } from 'redux-persist/integration/react';
ReactDOM.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>,
document.getElementById('root')
);
十三 React-Redux开发调试应用
https://github.com/zalmoxisus/redux-devtools-extension
需要理解的概念有:
- 如何更好的进行项目调用
- 第三方插件的集成都在store中进行管理
src/store/index.js
import { createStore, applyMiddleware } from 'redux';
import reducers from '../reducers';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import { composeWithDevTools } from 'redux-devtools-extension';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
// defaults to localStorage for web
const persistConfig = {
key: 'root',
storage,
};
const persistedReducer = persistReducer(persistConfig, reducers);
export const store = createStore(persistedReducer,
composeWithDevTools(applyMiddleware(thunk, logger)));
export const persistor = persistStore(store);
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { Provider } from 'react-redux';
import { store, persistor } from './store'; //从一个对象变成两个对象
import App from './App';
import { PersistGate } from 'redux-persist/integration/react';
ReactDOM.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>,
document.getElementById('root')
);
十三 React-Redux开发调试应用
https://github.com/zalmoxisus/redux-devtools-extension
需要理解的概念有:
- 如何更好的进行项目调用
- 第三方插件的集成都在store中进行管理
src/store/index.js
import { createStore, applyMiddleware } from 'redux';
import reducers from '../reducers';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import { composeWithDevTools } from 'redux-devtools-extension';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
// defaults to localStorage for web
const persistConfig = {
key: 'root',
storage,
};
const persistedReducer = persistReducer(persistConfig, reducers);
export const store = createStore(persistedReducer,
composeWithDevTools(applyMiddleware(thunk, logger)));
export const persistor = persistStore(store);