前言

如果还不知道为什么要使用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>&nbsp;&nbsp;
<button onClick={this.decrement}>-</button>&nbsp;&nbsp;
</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的更多相关文章

  1. React进阶篇学习

    继续上一次基础篇, 分享一些关于React的进阶技术 React 进阶部分 ** context ** ** setState vs forceUpdate ** ** Mixins ** ** HO ...

  2. React进阶篇(1) -- react-router4模块化

    本篇内容: 单一的路由无嵌套 多层嵌套路由 获取路径中的参数 按需加载 单一的路由无嵌套 routers.js import Home from 'components/Home'; import N ...

  3. React进阶之高阶组件

    前言 本文代码浅显易懂,思想深入实用.此属于react进阶用法,如果你还不了解react,建议从文档开始看起. 我们都知道高阶函数是什么, 高阶组件其实是差不多的用法,只不过传入的参数变成了react ...

  4. Membership三步曲之进阶篇 - 深入剖析Provider Model

    Membership 三步曲之进阶篇 - 深入剖析Provider Model 本文的目标是让每一个人都知道Provider Model 是什么,并且能灵活的在自己的项目中使用它. Membershi ...

  5. idea 插件的使用 进阶篇

    CSDN 2016博客之星评选结果公布    [系列直播]零基础学习微信小程序!      "我的2016"主题征文活动   博客的神秘功能 idea 插件的使用 进阶篇(个人收集 ...

  6. 2. web前端开发分享-css,js进阶篇

    一,css进阶篇: 等css哪些事儿看了两三遍之后,需要对看过的知识综合应用,这时候需要大量的实践经验, 简单的想法:把qq首页全屏另存为jpg然后通过ps工具切图结合css转换成html,有无从下手 ...

  7. windows系统快捷操作の进阶篇

    上次介绍了windows系统上一些自带的常用快捷键,有些确实很方便,也满足了我们的一部分需求.但是我们追求效率的步伐怎会止步于此?这一次我将会进一步介绍windows上提升效率的方法. 一:运行 打开 ...

  8. python 面向对象(进阶篇)

    上一篇<Python 面向对象(初级篇)>文章介绍了面向对象基本知识: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用 类 是一个模板,模板中包装了多个“函数”供使 ...

  9. 最快让你上手ReactiveCocoa之进阶篇

    前言 由于时间的问题,暂且只更新这么多了,后续还会持续更新本文<最快让你上手ReactiveCocoa之进阶篇>,目前只是简短的介绍了些RAC核心的一些方法,后续还需要加上MVVM+Rea ...

随机推荐

  1. Ehcache入门介绍一

    EhCache 是一个纯Java的进程内缓存框架,具有快速.精干等特点,是Hibernate中默认的CacheProvider. 特性有: 1. 快速轻量 Ehcache的线程机制是为大型高并发系统设 ...

  2. pymysql增删改查

    #!/usr/bin/env python # encoding: utf-8  # Date: 2018/6/24 # 1.增删改import pymysql conn = pymysql.conn ...

  3. 分布式队列 Celery

    详情参见: 分布式队列神器 Celery 用户指南(User Guide) 1) Celery-4.1 用户指南: Application(应用) 2) Celery-4.1 用户指南: Task(任 ...

  4. Deep Learning 学习笔记(7):神经网络的求解 与 反向传播算法(Back Propagation)

    反向传播算法(Back Propagation): 引言: 在逻辑回归中,我们使用梯度下降法求参数方程的最优解. 这种方法在神经网络中并不能直接使用, 因为神经网络有多层参数(最少两层),(?为何不能 ...

  5. 优化深度神经网络(三)Batch Normalization

    Coursera吴恩达<优化深度神经网络>课程笔记(3)-- 超参数调试.Batch正则化和编程框架 1. Tuning Process 深度神经网络需要调试的超参数(Hyperparam ...

  6. JQUEY 引用

    $(document).ready(function(){ alert($("a:first").attr("id")); });

  7. C# web程序,winform程序,控制台程序配置log4net,使用log4net

    第一添加log4net.config,这里配置包括信息提示写入,错误信息写入,控制台消息展示 <?xml version="1.0" encoding="utf-8 ...

  8. 敏捷软件开发Note

    [敏捷原则] 1.我们最优先要做的是通过尽早的.持续的交付有价值的软件为使客户满意. 初期交付的系统中所包含的功能越少,最终交付的系统的质量就越高.交付的越频繁,最终的产品质量就越高.敏捷实践会说早地 ...

  9. SpringAOP03 项目脚手架、自定义注解、织入切面、引介增强

    1 项目脚手架 利用 Maven 进行创建 1.1 利用IDEA创建一个Maven原型项目 技巧01:原型Maven项目是没有webapp文件夹和resources项目文件夹的,需要自己手动创建:创建 ...

  10. swift基本运算

    swift运算有单目运算,双目运算和三元运算 1:赋值操作 iX = iY//iX is 8 元组赋值 let (iX, iY = (, ) // iX is 8, iY is 7 和c语言不同的是, ...