Redux 和 React-redux

redux 官网

先来看看 React 一些特点和没有解决的问题:

  • 通信:组件之间如何通信?react 采用传参,对于大应用,很不方便。
  • 数据流:数据如何和视图串联起来?路由和数据如何绑定?如何编写异步逻辑?等等

为了解决这些问题引入 redux,前几天看见一个博客,把 redux 的原理结合动图把为什么使用 redux 讲的特别透彻。

一定要看:Redux设计思想与使用场景

同时配上这篇:redux 设计思想

总结一下就是:

  • redux 的诞生是为了给 React 应用提供「可预测化的状态管理」机制。

  • Redux 会将整个应用状态(其实也就是数据)存储到到一个地方,称为 store。

  • 这个 store 里面保存一棵状态树(state tree)

  • 组件改变 state 的唯一方法是通过调用 store 的 dispatch 方法,触发一个 action,这个action 被对应的 reducer 处理,于是 state 完成更新。

  • 组件可以派发 (dispatch) 行为 (action) 给 store,而不是直接通知其它组件

  • 其它组件可以通过订阅 store 中的状态 (state) 来刷新自己的视图

一、初识 Redux

网站重要的开门注:

redux a predictable state container for JavaScript apps.
Redux是一个可预测状态容器。

为了便于直观理解,演示先不配合 React 使用,全部在一个文件 1.js 里面写。使用 node 进行调试。

先安装 redux。

npm install --save redux

实例代码如下:

const redux = require("redux");

//①创建一个纯函数reducers。
//这个函数接收两个参数:一个 state、一个 action,并返回新的 state。
const reducers = (state = {a : 10},action)=>{
    if(action.type === "ADD"){
        return {
            a : state.a + 1
        }
    }else if(action.type === "UNADD"){
        return {
            a : state.a - 1
        }
    }
    return state;
}
// ②创建store
const store = redux.createStore(reducers);

// ③通过getState()方法可访问state
console.log(store.getState().a);//10

// ④通过dispatch方法{type:"ADD"}进行一次加法运算
store.dispatch({type:"ADD"});
console.log(store.getState().a);//11

// ⑤通过dispatch方法{type:"UNADD"}进行一次减法运算
store.dispatch({type:"UNADD"});
console.log(store.getState().a);//10
  1. redux 依赖于一个纯函数(reducer),它描述了 action 如何将 state 转为新的 state,action 只说明了什么事情发生,但是不会描述 state 如何改变。绝对不能改变 state 对象,如果你要变,只能返回新的 state。什么是纯函数?不改变传入的参数的函数就是纯函数。
  2. redux 通过 createStore 让 store 和 reducers 产生联系。
  3. 产生联系的 store ,提供一个 getState() 方法来访问纯函数的 state,通过 dispatch({"type":...}) 来检测命令。唯一能够改变 state 的方法就是 dispatch 一个 action。store 就是统一管理 reducer 和 action 的,将所有的一切统一起来的。
  4. store 有三个主要的功能:
  • 持有 app 的 state
  • 允许通过 getState() 得到 state
  • 允许通过 dispatch 来改变 state

注意的是:可以有多个纯函数,但只能有 1 个 store 。

  1. 我们知道 action 是 store 唯一的信息的来源且 action 需要被 dispatch() 函数派发,action 是纯的、扁平的 JavaScript 对象。

如果需要多个纯函数,则需要引入,combineReducers() 这个方法进行纯函数合并。

const redux = require("redux");

//①创建一个纯函数reducers。
const reducers = (state = {a : 10},action)=>{
    if(action.type === "ADD"){
        return {
            a : state.a + 1
        }
    };
    return state;
}
//⑥创建另一个纯函数reducers2。
const reducers2 = (state = {a : 100},action)=>{
    if(action.type === "LOAD"){
        return {
            a : action.palyload
        }
    }
    return state;
}
// ⑦多个需求只能拆分纯函数reducer,通过combineReducers合并成一个
const reducer = redux.combineReducers({
    reducers,
    reducers2
})
// ②创建store
const store = redux.createStore(reducer);

// ③通过getState()方法可访问state的加命名空间
console.log(store.getState().reducers.a);//10

// 但是通过dispatch却不用添加命名空间
store.dispatch({type:"ADD"});
console.log(store.getState().reducers.a);//11
//<============华丽的分割线===========>

// ③通过getState()方法可访问state的加命名空间
console.log(store.getState().reducers2.a);//100

// action可通过playload携带负载参数
store.dispatch({type:"LOAD",palyload:"我是携带的负载!"});
console.log(store.getState().reducers2.a);//我是携带的负载

纯函数里面的 action 就是由 type 属性的组成的 JSON,但实际上不仅仅有 type 属性,还可以有其他的属性,所有其他的属性都叫做载荷(payload)。

二、redux 结合 React

先安装:react-redux粘合剂官网:https://react-redux.js.org/

npm install --save react-redux

梳理一下 redux 和 react-redux 提供各自提供的 API。

  • Redux 是可预测状态容器(reducer、state、store、dispatch、action、applyMiddleware 等API)
  • react-redux 提供 Provider 和 connect 。

再看项目主要目录结构:

┣✈ main.js
┣✈ index.html
┣✈ webpack.config.js
┣✈ package.json
┗✈ views
      ┣✈ app
            ┣✈ App.js
      ┗✈ store
            ┣✈ index.js
            ┗✈ counterStore.js

展示项目目录代码:

  • couterStore.js 放入纯函数:
export default (state = {a : 10},action)=>{
    if(action.type === "ADD"){
        return {
            a : state.a + 1
        }
    }else if(action.type === "UNADD"){
        return {
            a : state.a - 1
        }
    };
    return state;
}

使用 redux 第一步必须是写纯函数。

  • index.js
import counterStore from "./counterStore.js";
import {combineReducers} from "redux";
export default combineReducers({
    counterStore
});

使用 combineReducers 进行纯函数合并,变成一个统领文件

  • main.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./views/app/App.js";
import {createStore} from "redux";
import {Provider} from "react-redux";
import reducers from "./views/store";

const store = createStore(reducers);

ReactDOM.render(
    <Provider store={store}>
        <App/>
    </Provider>
    ,
    document.getElementById("app")
);

引入 createStore 函数、引入r educers 统领文件、创建 store、可以测试使用store.getState().a输出结果。Provider 可以让 store 照耀在 APP 里面所有的组件上,做到天下无人不识君。Provider 的机理是使用的 context 上下文机理。

  • App.js
    connect 这里是个高阶函数的用法,很明显 connect 进行了柯粒化处理。这里叫做装饰器。装饰器两个(mapStateToProps)(mapDispatchToProps),第一圆括号内部写如何装饰,映射全局 state 到组件的 props;第二个圆括号内部写装饰谁,将dispatch这个词语映射到 props 上。
    写法一:不使用装饰器
import React,{Component} from "react";
import {connect} from "react-redux";

class App extends Component {
    constructor(){
        super()
    }
    render(){
        return(
            <div>
                <h1>{this.props.a}</h1>
                <button onClick = {this.props.add}>按我加一</button>
            </div>
        );
    }
}
export default connect(
    ({counterStore})=>({
        a : counterStore.a
    }),
    dispatch=>({
        add(){
            dispatch({type:"ADD"});
        }
    })
)(App);

写法二:装饰器写法
react 不支持装饰器写法,首先安装语法糖。

npm install --save @babel/plugin-proposal-decorators

参考 babel 官网 @babel/plugin-proposal-decorators · Babel
webpack.config.js 进行配置:

{
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }]
  ]
}

配置完,重新开启项目即可使用,改写代码:

import React,{Component} from "react";
import {connect} from "react-redux";
@connect(
    ({counterStore})=>({
        a : counterStore.a
    }),
    dispatch=>({
        dispatch
    })
)
export default class App extends Component {
    constructor(){
        super()
    }
    render(){
        return(
            <div>
                <h1>{this.props.a}</h1>
                <button onClick = {()=>{this.props.dispatch({type:"ADD"})}}>按我加一</button>
            </div>
        );
    }
}

两者实现的效果都是一样的。打开http://127.0.0.1:8080/,查看效果:

三、bindActionCreators(基本不用)

Action Creator 就是一个创建 action 的函数,例如:() => ({“type” : “ADD”})。不要混淆 action 和 action creator 这两个概念。Action 是一个信息的负载,而 action creator 是一个创建 action 的工厂。调用 action creator 只会生产 action,但不分发。bindActionCreators 这个 API 就是使用 Action Creator。

使用场景?所以是基本没用。

惟一会使用到 bindActionCreators 的场景是当你需要把 action creator 往下传到一个组件上,却不想让这个组件觉察到 Redux 的存在,而且不希望把 dispatch 或 Redux store 传给它。

bindActionCreators(actionCreators, dispatch)
参数

  1. actionCreators (Function or Object): 一个 action creator,或者一个 value 是 action creator 的对象。

  2. dispatch (Function): 一个由 Store 实例提供的 dispatch 函数。

更改 App.js

import React,{Component} from "react";
import {connect} from "react-redux";
import * as actionCreators from "./actionCreators.js";
import {bindActionCreators} from "redux";
@connect(
    ({counterStore})=>({
        a : counterStore.a
    }),
    (dispatch) => ({
        actionCreators: bindActionCreators(actionCreators , dispatch)
    })
)
export default class App extends Component {
    constructor(){
        super()
    }
    render(){
        return(
            <div>
                <h1>{this.props.a}</h1>
                <button onClick = {this.props.actionCreators.add}>按我加一</button>
            </div>
        );
    }
}
新增的actionCreators文件
export const add = ()=>({type:"ADD"});

加法器仍然起作用。

四、安装 redux-logger

redux-logger 是每次 dispatch 的记录器。当我们启用 logger 的时候每次 dispatch ,都会在控制台输出每次详情。而我们只需要一装一引一配
一装 redux-logger

npm install --save redux-logger

在主入口文件 main.js 中进行 一引一配

import React from "react";
import ReactDOM from "react-dom";
import App from "./views/app/App.js";
import {createStore,applyMiddleware} from "redux";
import {Provider} from "react-redux";
import reducers from "./views/store";
import logger from "redux-logger";
const store = createStore(reducers,applyMiddleware(logger));

ReactDOM.render(
    <Provider store={store}>
        <App/>
    </Provider>
    ,
    document.getElementById("app")
);

输入网址:http://127.0.0.1:8080/ 查看控制台效果。

五、redux 的 hooks

hooks 在 v7.1.0 中首次被添加。这有一篇很好的文章,来介绍新出的 hook API 。
【译】React Redux API Hooks

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容