React进阶篇(2) -- Redux
前言
如果还不知道为什么要使用Redux,说明你暂时还不需要它。
三大原则
单一数据源
整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
State 是只读的
唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
使用纯函数来执行修改
为了描述 action 如何改变 state tree ,你需要编写 reducers。
Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state。刚开始可以只有一个 reducer,随着应用变大,可以把它拆成多个小的 reducers。
小结
可以用普通对象来描述应用的 `state`;
要想更新 state 中的数据,需要发起一个 `action`。Action 就是一个普通 JavaScript 对象;
Action主要包含2个方面的属性:
type:标识属性,字符串,唯一,必要属性
xxx:数据属性,任意类型,可选属性
强制使用 action 来描述所有变化带来的好处是可以清晰地知道应用中到底发生了什么。
准备
npm install --save redux
npm install --save react-redux //react绑定库
npm install --save-dev redux-devtools-extension //调试相关
官方例子
先来一个官方的例子,了解一下大致的工作情况。
import { createStore } from 'redux';
/**
* 这是一个 reducer,形式为 (state, action) => state 的纯函数。
* 描述了 action 如何把 state 转变成下一个 state。
*
* state 的形式取决于你,可以是基本类型、数组、对象、
* 甚至是 Immutable.js 生成的数据结构。惟一的要点是
* 当 state 变化时需要返回全新的对象,而不是修改传入的参数。
*
* 下面例子使用 `switch` 语句和字符串来做判断,但你可以写帮助类(helper)
* 根据不同的约定(如方法映射)来判断,只要适用你的项目即可。
*/
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
// 创建 Redux store 来存放应用的状态。
// API 是 { subscribe, dispatch, getState }。
let store = createStore(counter);
// 可以手动订阅更新,也可以事件绑定到视图层。
store.subscribe(() =>
console.log(store.getState())
);
// 改变内部 state 惟一方法是 dispatch 一个 action。
// action 可以被序列化,用日记记录和储存下来,后期还可以以回放的方式执行
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch({ type: 'DECREMENT' });
// 1
实例分析
经过上面的介绍应该对Redux有了一定的了解。
到了这里很多人都会想,官方demo都能跑,概念也大致明白……
但是,实际项目怎么做!?
实例一:
介绍:单纯的redux的写法可以参考上面的官方写法,这里配合react-redux使用,减少一些繁琐的操作。
相关目录:
src/
├── index.js
├── store
│ ├── action-type.js //声明判断的常量的集合便于统一管理
│ ├── actions.js //action集合,更新状态的指令
│ ├── reducers.js //reducer集合,更新状态的具体逻辑
│ └── store.js //创建 Redux store 来存放应用的状态
└── components
action-type.js
export const INCREMENT='INCREMENT';
export const DECREMENT='DECREMENT';
actions.js
import {INCREMENT,DECREMENT} from './action-types'
export const increment=(num)=>({type:INCREMENT,data:num }); //此处统一使用data方便获取,个人习惯
export const decrement=(num)=>({type:DECREMENT,data:num})
reducers.js
import {INCREMENT,DECREMENT} from "./action-types";
export function counter(state=0,action){
switch (action.type){
case INCREMENT:
return state+action.data; //此处统一使用data方便获取,个人习惯
case DECREMENT:
return state-action.data;
default:
return state;
}
}
store.js
import {createStore } from 'redux';
import {reducers} from "./reducers";
const store = createStore(
reducers,
//这里使用非侵入式调试,需npm install --save-dev redux-devtools-extension
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
index.js
import {Provider} from 'react-redux';
import store from './redux/store';
import App from './App';
ReactDOM.render(<Provider store={store}><App /></Provider>,document.getElementById('root'));
App.jsx
import React, { Component } from 'react';
import Pro2 from './components/Pro2';
import { connect} from 'react-redux';
class App extends Component {
render() {
let {count}=this.props;
return (
<div className="App">
<p>App : click {count} times</p>
<Pro2 />
</div>
);
}
}
export default connect(
state=>({count:state}),
{}
)(App);
Pro2.jsx
import React, {Component} from 'react';
import { connect} from 'react-redux';
<!--引入更新状态的方法-->
import {increment,decrement} from '../redux/actions';
class Pro2 extends Component {
<!--更新redux中的状态-->
increment=()=>{
<!--在指定方法中传入参数-->
this.props.increment(1);
}
decrement=()=>{
this.props.decrement(2);
}
render() {
const {count}=this.props; //获取redux中的状态
return (
<div className='name'>
<p>Pro2 : click {count} times</p>
<div>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
</div>
)
}
}
export default connect( //将react与redux关联起来
state=>({count:state}), //获取redux中的状态,指定对应的接收props名字
{increment,decrement} //绑定action中更新状态的方法
)(Pro2);
效果如图:

实例二:
当有多个reducer时有些许的变化:
action-type.js,actions.js,store.js与上面相同
reducers.js
注意暴露出去的名字,在组件中通过store.getState().xxx可以获取到对应状态
import {combineReducers} from 'redux';
import {INCREMENT,DECREMENT,UPDATEUSERINFO} from "./action-types";
const counter=(state=0,action)=>{
switch (action.type){
case INCREMENT:
return state+action.data;
case DECREMENT:
return state-action.data;
default:
return state;
}
}
// 用户初始化信息
const userInfoInit={
name:'adoctors',
headPic:'xxx',
tel:'130xxxxxxx2'
}
// 更新用户状态
const userInfo=(state=userInfoInit,action)=>{
switch (action.type) {
case UPDATEUSERINFO:
return ({...state,...action.data});
default:
return state;
}
}
export default combineReducers({
userInfo,
counter
})
组件中关联时的改变:
export default connect(
state=>({userInfo:state.userInfo}), //可以指定需要的某个状态
{changeLogin}
)(Header)
store.js
import {createStore } from 'redux';
import {reducers} from "./reducers";
const store = createStore(
reducers,
//这里使用非侵入式调试,需npm install --save-dev redux-devtools-extension
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
redux状态持久化
如果用过vuex的会知道这种状态管理遇到页面刷新时,状态会丢失,会回到初始状态;
那么redux有没有这样的问题呢?
恭喜恭喜!真!的!有!
所以redux状态的持久化其实时使用的基本需求。
npm install redux-persist
store.js
import React from 'react';
import {createStore ,applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import {persistStore, persistReducer} from 'redux-persist'; //引入固定方法
import storage from 'redux-persist/lib/storage/session'; //存储方式
import reducers from './reducers';
<!--相关配置-->
const config = {
key: 'root',
storage
};
let reducer = persistReducer(config, reducers);
let store = createStore(reducer, applyMiddleware(thunk));
let persistor = persistStore(store);
export { persistor, store }
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import { persistor, store } from './store/store';
import { PersistGate } from 'redux-persist/es/integration/react'; //特定标签
import App from './containers/App';
ReactDOM.render(<Provider store={store}>
<PersistGate persistor={persistor}>
<App />
</PersistGate>
</Provider>, document.getElementById('root'));
改变redux状态时会同时生成相应的缓存:

存储的加密处理
cnpm i -S redux-persist-transform-encrypt
store.js
import createEncryptor from 'redux-persist-transform-encrypt';
const encryptor = createEncryptor({
secretKey: 'xxx',
onError: function(err) {
console.log(err)
}
})
let reducer = persistReducer({
key: 'root',
storage,
transforms: [encryptor]
},
reducers
)
let store = createStore(reducer, applyMiddleware(thunk));
参考文档:
https://www.redux.org.cn/docs/introduction/Motivation.html
https://github.com/zalmoxisus/redux-devtools-extension
https://www.npmjs.com/package/redux-persist
https://github.com/maxdeviant/redux-persist-transform-encrypt
React进阶篇(2) -- Redux的更多相关文章
- React进阶篇学习
继续上一次基础篇, 分享一些关于React的进阶技术 React 进阶部分 ** context ** ** setState vs forceUpdate ** ** Mixins ** ** HO ...
- React进阶篇(1) -- react-router4模块化
本篇内容: 单一的路由无嵌套 多层嵌套路由 获取路径中的参数 按需加载 单一的路由无嵌套 routers.js import Home from 'components/Home'; import N ...
- React进阶之高阶组件
前言 本文代码浅显易懂,思想深入实用.此属于react进阶用法,如果你还不了解react,建议从文档开始看起. 我们都知道高阶函数是什么, 高阶组件其实是差不多的用法,只不过传入的参数变成了react ...
- Membership三步曲之进阶篇 - 深入剖析Provider Model
Membership 三步曲之进阶篇 - 深入剖析Provider Model 本文的目标是让每一个人都知道Provider Model 是什么,并且能灵活的在自己的项目中使用它. Membershi ...
- idea 插件的使用 进阶篇
CSDN 2016博客之星评选结果公布 [系列直播]零基础学习微信小程序! "我的2016"主题征文活动 博客的神秘功能 idea 插件的使用 进阶篇(个人收集 ...
- 2. web前端开发分享-css,js进阶篇
一,css进阶篇: 等css哪些事儿看了两三遍之后,需要对看过的知识综合应用,这时候需要大量的实践经验, 简单的想法:把qq首页全屏另存为jpg然后通过ps工具切图结合css转换成html,有无从下手 ...
- windows系统快捷操作の进阶篇
上次介绍了windows系统上一些自带的常用快捷键,有些确实很方便,也满足了我们的一部分需求.但是我们追求效率的步伐怎会止步于此?这一次我将会进一步介绍windows上提升效率的方法. 一:运行 打开 ...
- python 面向对象(进阶篇)
上一篇<Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使 ...
- 最快让你上手ReactiveCocoa之进阶篇
前言 由于时间的问题,暂且只更新这么多了,后续还会持续更新本文<最快让你上手ReactiveCocoa之进阶篇>,目前只是简短的介绍了些RAC核心的一些方法,后续还需要加上MVVM+Rea ...
随机推荐
- java成神之——java常识
java常识 简单DOS命令 java基础名词 java编译和运行流程 Eclipse常用快捷键 Eclipse相关操作 java基本数据类型 java基本数据类型转换 java常用运算符 java流 ...
- pandas中DataFrame相关
1.创建 1.1 标准格式创建 DataFrame创建方法有很多,常用基本格式是:DataFrame 构造器参数:DataFrame(data=[],index=[],coloumns=[]) In ...
- Oracle 中 over() 函数
:first-child { margin-top: 0; } blockquote > :last-child { margin-bottom: 0; } img { border: 0; m ...
- codeforce 457DIV2 C题
题意 你需要构造一个n个点m条边的无向有权图,要求这个图的MST中边权的和与从1到n的最短路长度都为素数 分析 可以想到这样一种贪心,在i到i+1直接连一条边,这样最短路和MST都会是同样的一些边.只 ...
- SpringBoot:阿里数据源配置、JPA显示sql语句、格式化JPA查询的sql语句
1 数据源和JPA配置 1.1 显示sql配置和格式化sql配置 者两个配置都是属于hibernate的配置,但是springdatajpa给我们简化了:所有hibernate的配置都在jpa下面的p ...
- spring框架 事务 注解配置方式
user=LF password=LF jdbcUrl=jdbc:oracle:thin:@localhost:1521:orcl driverClass=oracle.jdbc.driver.Ora ...
- PyGrub
from:https://wiki.debian.org/PyGrub Using pyGRUB on Wheezy to boot a domU kernel Using pyGRUB from x ...
- mybatis学习笔记 spring与mybatis整合
转载自http://blog.csdn.net/naruto_Mr/article/details/48239357 1.创建web工程,导入spring依赖包与mybatis依赖包,还需要mybat ...
- 使用HttpServletRequestWrapper修改请求参数 和 使用HttpServletResponseWrapper截获响应数据
Servlet规范中的Filter引入了一个功能强大的拦截模式.Filter能在request到达servlet的服务方法之前拦截request对象,而在服务方法转移控制后又能拦截response对象 ...
- SpringMVC——文件的上传
一.加入依赖 commons-io-2.0.jar commons-fileupload-1.2.1.jar 二.接口MultipartResolver Spring MVC 为文件上传提供了直接的支 ...