前言

如果还不知道为什么要使用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. 解决sever 2008中tomcat的报错 init Failed to initialize end point associated with ProtocolHandler ["http-nio-80"]

    错误现象: 01-Aug-2017 14:59:50.140 信息 [main] org.apache.coyote.AbstractProtocol.init Initializing Protoc ...

  2. 关于 NULL的坑

    有如下的表: select * from testtable where name in ('name'):  结果是第一条: select * from testtable where name n ...

  3. springboot成神之——spring文件下载功能

    本文介绍spring文件下载功能 目录结构 DemoApplication WebConfig TestController MediaTypeUtils 前端测试 本文介绍spring文件下载功能 ...

  4. day8-心得

    1. Socket介绍 概念 A network socket is an endpoint of a connection across a computer network. Today, mos ...

  5. 四道腾讯的javascript面试题

    今天整理自己的文件夹时,发现了曾经面试腾讯实习生时的被问挂了的面试题,我将它们整理好了供大家借鉴.当时要求的是手写代码.汗啊,我那时弱爆了! 1,将给定数组转换为一个随机数组 Array.protot ...

  6. 前端自动化之webstrom

    前端自动化之webstrom 在刚接触前端的时候,使用的就一直是webstrom,版本是webstrom 8. 前端自动画使用的时候,因为webstrom 8版本是没有集成gulp的.所以每次使用都默 ...

  7. leetcode582

    public class Solution { public IList<int> KillProcess(IList<int> pid, IList<int> p ...

  8. html收藏

    全屏显示<input type="button" name="fullscreen" value="全屏显示" onclick=&qu ...

  9. cmd命令删除文件及文件夹

    rmdir /s/q wenjianming 其中: /s 是代表删除所有子目录跟其中的档案. /q 是不要它在删除档案或目录时,不再问我 Yes or No 的动作.

  10. Python常用算法

    本节内容 算法定义 时间复杂度 空间复杂度 常用算法实例 1.算法定义 算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机 ...