Redux

  • Redux 解决了复杂应用的状态管理问题,可以跨层级任意传递数据
    组件直接与store通信

如果项目中应用了多层级嵌套的一些组件,需要跨层级传递数据的场景很多,那么利用props的方式传数据将会很麻烦,需要父子组件一层层传,数据传来传去还可能容易产生bug,不易维护,那么使用Redux来管理状态就是为了解决了这个问题

  • Redux是一个经典的发布-订阅器
// 发布订阅的一个例子
Button.addEventListener('click', ()=>{}) 订阅 当触发A的时候 就执行B
Button.removeEventListener('click',()=>{}) 取消订阅 当触发A的时候 不再执行B
Button.onClick //发布 - 触发A
  • Redux和react-redux是两个不同的库,react-redux将redux和react连接起来
  • redux仅支持同步数据流,可用中间件thunk实现异步数据流

文件夹结构


image.png

Store 就是保存数据的地方,你可以把它看成一个容器。整个应用只能有一个 Store。
Store对象包含所有数据。如果想得到某个时点的数据,就要对 Store 生成快照。这种时点的数据集合,就叫做 State。当前时刻的 State,可以通过store.getState()拿到。Redux 规定, 一个 State 对应一个 View。只要 State 相同,View 就相同。你知道 State,就知道 View 是什么样,反之亦然。
action是一个对象,用字符串变量类型的type字段表示action的名称, 是store数据的唯一来源,Action 就是 View 发出的通知,表示 State 应该要发生变化了。

action demo.ts

import {createAction} from 'redux-actions';
import { DECREMENT_COUNT, INCREMENT_COUNT } from '../actionTypes/demo';

export interface IAction<T> {
    type: string;
    payload: T;
}

//createStore函数接受另一个函数作为参数,返回新生成的 Store 对象。()=>({type: XXX})
export const decrement = createAction(DECREMENT_COUNT);
export const increment = createAction(INCREMENT_COUNT);

actionType demo.ts
当应用规模越来越大时,建议使用单独的模块或文件来存放 action。

export const INCREMENT_COUNT = 'INCREMENT_COUNT';
export type INCREMENT_COUNT = typeof INCREMENT_COUNT;

export const DECREMENT_COUNT = 'DECREMENT_COUNT';
export type DECREMENT_COUNT = typeof DECREMENT_COUNT;

reducer demo.ts
Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。
Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
Reducer 函数最重要的特征是,它是一个纯函数。也就是说,只要是同样的输入,必定得到同样的输出。由于 Reducer 是纯函数,就可以保证同样的State,必定得到同样的 View。但也正因为这一点,Reducer 函数里面不能改变 State,必须返回一个全新的对象。

function reducer(state,action) {
  return {...state,...newState}
}
import {handleActions} from 'redux-actions';
import { IAction } from '../actions/demo';
import { INCREMENT_COUNT, DECREMENT_COUNT } from '../actionTypes/demo';

export interface IDemoState {
    val: number; 
}

//state初始值
const createInitialState: () => IDemoState = () => ({
    val: 0
})

const demo = handleActions({
    [INCREMENT_COUNT](state: IDemoState, action: IAction<IDemoState>):IDemoState {
        const {payload} = action;
        return {
            ...state,
            val: state.val + 1
        }
    },
    [DECREMENT_COUNT](state: IDemoState, action: IAction<IDemoState>):IDemoState {
        const {payload} = action;
        return {
            ...state,
            val: state.val - 1
        }
    }
}, createInitialState())

export default demo;

Redux 提供了一个combineReducers方法,用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。
这种写法有一个前提,就是 State 的属性名必须与子 Reducer 同名。
总之,combineReducers()做的就是产生一个整体的 Reducer 函数。该函数根据 State 的 key 去执行相应的子 Reducer,并将返回结果合并成一个大的 State 对象。
reducer index.tx

import {combineReducers} from 'redux';
import demo from './demo';

const rootReducer = combineReducers({demo});

export default rootReducer;

index.ts

//redux默认的dispatch默认接收一个object类型的action,所以action不能是异步,引入中间件Redux-thunk可以来解决异步问题
//中间件就是一个函数,对store.dispatch方法进行了改造,在发出 Action 和执行 Reducer 这两步之间,添加了其他功能。
import {createStore, applyMiddleware, compose} from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from './reducers/index';
//compose会将传入的函数数组从右向左串联执行
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const enhancer = composeEnhancers(applyMiddleware(thunkMiddleware));
//applyMiddleware用于调用各种中间件
export default createStore (
    rootReducer,
    enhancer
)
//store.dispatch方法会触发 Reducer 的自动执行,为此,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 传入createStore方法。
// 上面代码中,createStore接受 Reducer 作为参数,生成一个新的 Store。以后每当store.dispatch发送过来一个新的 Action,就会自动调用 Reducer,得到新的 State。
// 调用createStore方法创建一个store对象,可以接收三个参数,reducer,preloadedState(在服务端渲染时用),enhancer(中间件),
//store是保存数据的地方,一个项目只有一个store

App.tsx

import { Provider } from 'react-redux';
import store from './store/index';
import Counter from './components/counter';
import 'antd/dist/antd.css';
// Provider是通过context api把数据往下传
//react有三种数据:context:向子组件,孙组件传递数据,props:父组件向子组件传递数据,state:组件自身数据
function App() {
  return (
    <Provider store ={store}>
      <Counter />
    </Provider>
  );
}
//Provider包裹跟组件 传入store对象
export default App;

Counter.tsx

import React from "react";
import {connect} from "react-redux";
import {increment, decrement} from '../store/actions/demo';
import {Button} from 'antd';

interface IProps {
    val: number;
    addData: (data:any) => void;
    reduceData: (data:any) => void;
}

const Counter = (props:IProps) => {
    const {val,addData,reduceData} = props;

    return (
        <div>
            <Button onClick={addData}>加一</Button>
            <Button onClick={reduceData}>减一</Button>
            {val}
        </div>
    );
}

//定义value到state的映射,以及addData reduceData到dispatch的映射。
//将state注入组件props里
const mapStateToPorps = (state: any) => ({
    val: state.demo.val
})
//将action注入组件props里
const mapDispatchToProps = (dispatch: any) => ({
    addData(data:any):void {
        dispatch(increment())
//dispatch()是 View 发出 Action 的唯一方法。接受一个 Action 对象作为参数,将它发送出去。
    },
    reduceData(data:any):void {
        dispatch(decrement())
    }
})
export default connect(mapStateToPorps, mapDispatchToProps)(Counter);
//组件引入 connect方法,将需要更新的组件连接起来,并传入mapStateToProps,mapDispatchToProps
//connect是一个高阶组件,接收Provider传递过来的store对象,订阅store中的数据,如果store中的数据发生变化,调用setState触发组件更新

首先,用户发出 Action。
然后,Store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action。 Reducer 会返回新的 State 。
State 一旦有变化,Store 就会调用监听函数。通过store.getState()得到当前状态。如果使用的是 React,这时可以触发重新渲染 View。
定义state,dispatch的映射。
然后,使用connect方法生成容器组件。
然后,定义这个组件的 Reducer。
最后,生成store对象,并使用Provider在根组件外面包一层。

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

推荐阅读更多精彩内容