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 ...
随机推荐
- Linux MTD系统剖析
MTD,Memory Technology Device即内存技术设备,在Linux内核中,引入MTD层为NOR FLASH和NAND FLASH设备提供统一接口.MTD将文件系统与底层FLASH存储 ...
- cocos2d-x v3.3开发环境配置
开发环境: Win10 VS2012 链接:http://pan.baidu.com/s/1bn6S31d 密码:j2ol 配置Java SDK: 新建:JAVA_HOME =G:\Java\jdk1 ...
- 配置VMware中的Ubuntu能够被其他机器ssh远程
配置虚拟机Ubuntu能够被其他机器 ssh远程 将虚拟机Ubuntu改成桥接模式 在Ubuntu中安装openssh sudo apt install openssh-server -y sudo ...
- 「小程序JAVA实战」 小程序抽离公用方法进行模块化(12)
转自:https://idig8.com/2018/08/09/xiaochengxu-chuji-12/ 小程序的模块化,把砖磊成一个墩子,用的时候把整个墩子移走.js更好的调用,应用更加公用化.源 ...
- <正则吃饺子> :关于redis集群的搭建、集群测试、搭建中遇到的问题总结
项目中使用了redis ,对于其基本的使用,相对简单些,根据项目中已经提供的工具就可以实现基本的功能,但是只是这样的话,对于redis还是太肤浅,甚至刚开始时候,集群.多节点.主从是什么,他们之间是什 ...
- ubuntu主目录下的中文文件夹名改回英文
linux下经常用命令行,目录有中文输起来非常麻烦,想把他改回英文于是登录的时候选择英文发现没装英文语言环境,为这个重新装麻烦,只能再想办法 找了一下发现传话里有个用户文件夹更新,命令是xdg-use ...
- Winform绑定图片的三种方式
1.绝对路径: this.pictureBox2.Image=Image.FromFile("D:\\001.jpg"); 2.相对路径: Application.StartupP ...
- 通过helloworld来认识下backbone
Backbone主要涉及3部分:model,collection和view.而这个框架的优势在于:数据与视图分离,通过操作model来自动更新view. 根据我的个人经验,直接写个简单的例子是最最直观 ...
- nginx搭建文件服务器配置文件
worker_processes 1; events { worker_connections 1024;} http { include mime.types; default_type appli ...
- Linux ping不通外网
在linux中ping www.baidu.com 无法ping通,可能原因是DNS没配置好 方法一: 修改vi /etc/resolv.conf 增加如下内容: nameserver 114.11 ...